TextField wrapped in a Transform.translate does not focus when clicked - flutter

This has been very confusing to me since the TextField wrapped in a SizedBox only, works perfectly; but it appears to be a simple image (it can't be tapped, nor focused) when I wrap that same piece of code in a Transform.translate widget.
Also, if I change the Transform.translate to a Positioned widget, the TextField works perfectly, but I'd like to understand why this is happening, because I am required to use Transform.translate over Positioned for this special project.
class Login extends StatelessWidget {
Login({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xfff4f6fa),
body: Stack(
children: <Widget>[
SizedBox(
width: 302.0,
height: 60.0,
child: TextField(
decoration: InputDecoration(
hintText: 'Correo electrónico *',
prefixIcon: Icon(Icons.send),
border: OutlineInputBorder(
borderSide:
BorderSide(width: 1.0, color: const Color(0xffe7e7e7)),
borderRadius: BorderRadius.circular(4.0),
),
),
style: TextStyle(
fontFamily: 'Nunito',
fontSize: 14,
color: const Color(0xff777777),
height: 1.4285714285714286,
),
textAlign: TextAlign.left,
onChanged: (String value) async {
},
onSubmitted: (String value) async {
},
),
),
// Positioned(
Transform.translate(
offset: Offset(36.0, 317.8),
// left: 36,
// top: 317.8,
child:
// Adobe XD layer: 'input:mail' (component)
SizedBox(
width: 302.0,
height: 60.0,
child: TextField(
decoration: InputDecoration(
hintText: 'Correo electrónico *',
prefixIcon: Icon(Icons.send),
border: OutlineInputBorder(
borderSide:
BorderSide(width: 1.0, color: const Color(0xffe7e7e7)),
borderRadius: BorderRadius.circular(4.0),
),
),
style: TextStyle(
fontFamily: 'Nunito',
fontSize: 14,
color: const Color(0xff777777),
height: 1.4285714285714286,
),
textAlign: TextAlign.left,
onChanged: (String value) async {
},
onSubmitted: (String value) async {
},
),
),
),
],
),
);
}
}
As mentioned before, both inputs show on the screen (one at the center and one at the top) only the TextField wrapped with a SizeBox (first element of the Stack, at the top of the sreen)can be tapped, nothing happens when the one at the center of the screen (second element of the stack, located there because of the translation) is tapped.

Try debugging and see the layout grid lines and correct boundaries of widget using Flutter inspector.
That should give you a better idea at why is it behaving this way.
Seems like when You use the Transform widget, it doesnt register the hit as it merely moves the boundaries and not the tap area.
Try wrapping the Transform widget with a GestureDetector and see.
You can also follow a similar issue on GitHub:
https://github.com/flutter/flutter/issues/27587

Related

how to set icon button's splash radius based on it's parent widget height in flutter

I have created a customTextfield and placed IconButton as suffix icon,
here when I tap on icon button, its splash radius showing bigger than textfield,
here I want to fix height of splash radius based on it's parent.. like if it is inside of container of 100height..it must be set according to it...
here is my code
class CustomTextField extends StatelessWidget {
final String hint;
final bool isitpassword;
final TextEditingController controller;
const CustomTextField({Key? key,required this.hint,this.isitpassword=false,required this.controller}) : super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(20),
),
child: TextField(
style: TextStyle(
fontSize: 20,color: Colors.white,),
controller: controller,
obscureText: isitpassword,
decoration: InputDecoration(
border: InputBorder.none,
hintText: hint,
suffixIcon: IconButton(
//what spread radius to set for better view
icon: Icon(Icons.close,color: Colors.white,),onPressed: (){
controller.text='';
},),
),
)),
);
}
}
You can use splashRadius: 48 / 2
you can use InkWell instead like this it will take size as much as its parent:
TextField(
style: TextStyle(
fontSize: 20,
color: Colors.white,
),
controller: controller,
obscureText: isitpassword,
decoration: InputDecoration(
border: InputBorder.none,
hintText: hint,
suffixIcon: InkWell(
borderRadius: BorderRadius.circular(100),
child: Icon(
Icons.close,
color: Colors.white,
),
onTap: () {
controller.text = '';
},
)),
),
the 100 number is not important just set a big number.

Flutter DropdownButtonFormField icon doesn't align properly

Good morning, I am trying to align the DropDown's arrow icon with the hint text but I can't understand why the render engine positions it so far below.
I tried to set the InputDecoration's contentPadding property to EdgeInsets.zero and it seemed to work. However, it ruined the DropDown's focusBorder. Furthermore, I didn't understand what element the padding is attached to.
import 'package:flutter/material.dart';
class Example extends StatefulWidget {
const Example({Key? key}) : super(key: key);
#override
State<Example> createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.black87,
),
),
width: 135.0,
height: 55.0,
child: _buildField(),
);
}
Widget _buildField() {
return DropdownButtonFormField<String>(
icon: const Icon(
Icons.expand_more,
size: 30.0,
color: Color(0xFF2E2E2E),
),
hint: const Text(
'Status',
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w500,
color: Color(0xFFA6A6A6),
),
),
decoration: const InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.transparent,
width: 2.5,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFFF7321),
width: 2.5,
),
),
),
items: ['Item 1', 'Item 2', 'Item 3', 'Item 4']
.map<DropdownMenuItem<String>>(
(String item) => DropdownMenuItem(
value: item,
child: Text(item),
),
)
.toList(),
onChanged: (String? value) => print(value),
);
}
}
DropdownButton is a material component as seen on material.io.
This means that Flutter has every single styling hard-coded into the default styling like its defined on this website. It appears that by forcing this widget into a certain height:
return SizedBox(
height: 55.0,
child: Stack(...
you break the prebuilt styling of this widget. I have tested it with a height 50 to demonstrate this:
Here, you can see why setting the height is a bad idea on a Material Component.
Enough talking, what is the solution: Remove the height from SizedBox and any styling that is given by the material component:
return DropdownButtonFormField<String>(
icon: const Icon(
Icons.expand_more,
//size: 30.0,
color: Color(0xFF2E2E2E),
),
hint: const Text(
'Status',
style: TextStyle(
//fontSize: 18.0,
//fontWeight: FontWeight.w500,
color: Color(0xFFA6A6A6),
),
),
Now I know this will break your UI, but this is how Flutter works. Either you completely adopt the Material Design, or you will have a really bad time.
Try this example code I wrote for you.
My code:
DropdownButtonHideUnderline(
child: DropdownButtonFormField<String>(
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
icon: const Icon(
Icons.expand_circle_down, // Custom icon goes here.
),
iconSize: 30, // Icon height goes here.
hint: const Text('Choose item'),
value: _dropDownValue,
items: _dropDownList
.map(
(label) => DropdownMenuItem(
value: label,
child: Text(
label.toString(),
),
),
)
.toList(),
onChanged: (value) {
_dropDownValue = (value ?? _dropDownList[0]);
setState(() {});
},
),
),

Center align leading icon in Flutter

I need to align hint text and leading icon in the center, like shown here:
When I add a leading icon, and centre align decoration that is what I get. I need the icon to be in the centre as well.
Edit: Current code
TextField(
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: 'Type something',
prefixIcon: Icon(Icons.search)
)
),
There is a widget for your case: IntrinsicWidth. This widget is used to size its child to the child's intrinsic width.
Output
Full code:
class CenteredTextField extends StatefulWidget {
const CenteredTextField({Key key}) : super(key: key);
#override
_CenteredTextFieldState createState() => _CenteredTextFieldState();
}
class _CenteredTextFieldState extends State<CenteredTextField> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: 40.0,
margin: EdgeInsets.symmetric(horizontal: 20.0, vertical: 50.0),
decoration: BoxDecoration(
color: Colors.orange.withOpacity(0.4),
borderRadius: BorderRadius.circular(20.0),
border: Border.all(
color: Colors.orange,
width: 1.0,
),
),
child: Center(
child: IntrinsicWidth(
child: TextField(
textAlignVertical: TextAlignVertical.center,
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
hintText: 'Type verse address',
border: InputBorder.none,
),
),
),
),
),
);
}
}

iOS search bar animation in flutter

I want to add a animation in a search bar when the user taps on it (just like the animation in whatsApp or telegram [only in iOS])
here is the animation
I used Hero widget to do so but the widget is not working, I don't know why. Here is my code for it
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
//some widgets above the search bar
Hero(
tag: 'search',
child: Container(
height: 38,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32),
),
child: TextField(
cursorColor: Colors.blue,
decoration: InputDecoration(
hintStyle: TextStyle(fontSize: 16),
hintText: "Search here",
prefixIcon: Icon(Icons.search),
border: InputBorder.none,
),
),
),
),
//some more widgets here inside the column
])
This is part of code for 1st page and the other page is just the search bar.
This is the other page which is almost the same
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Hero(
tag: 'search',
child: Container(
height: 38,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32),
),
child: TextField(
cursorColor: Colors.blue,
decoration: InputDecoration(
hintStyle: TextStyle(fontSize: 16),
hintText: "Search here",
prefixIcon: Icon(Icons.search),
border: InputBorder.none,
),
),
),
),
),
);
}
Also is there any other way to do it?
EDIT: It's now working fine with Hero widget but it does not show that exact behaviour as in the above gif. How should I do that, if someone has any other method to achieve that, can also answer.
Hero is used when moving between different pages here(in the above animation) we are not navigating to any other page, so, remove the Hero widget
This can be done with Transform.translate in combination with Opacity() widget in Flutter.
Wrap the respective widget with Transform.translate create a Tween Animation and change its height with the help of Offset(dx, dy) (in this case dx will be 0 and dy will be the changing height), negative dy value will move the widget upwards. Also wrap it with Opacity() and change the value with the changing value of height.
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Transform.translate(
offset: Offset(0, animatedHeightFromTweenAnimation)
child: Opacity(
opacity: animatedHeightFromTweenAnimation/maxHeightValue;
child: anyWidget()
)
)
Container(
height: 38,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32),
),
child: TextField(
cursorColor: Colors.blue,
decoration: InputDecoration(
hintStyle: TextStyle(fontSize: 16),
hintText: "Search here",
prefixIcon: Icon(Icons.search),
border: InputBorder.none,
),
),
),//container
RaisedButton(
onPressed: () => controller.forward(), //here controller is animation controller
//the above line will start the animation
child: Text("press it"),
),
//some more widgets here inside the column
])

Validator error message changes TextFormField's height

When the error message shows up, it reduces the height of the TextFormField. If I understood correctly, that's because the height of the error message is taking into account in the height specified.
Here's a screen before :
and after :
Tried to put conterText: ' ' to the BoxDecoration (as I've seen on another topic) but it didn't help.
An idea ?
EDIT : OMG completly forgot to put the code, here it is :
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
height: 40.0,
child: _createTextFormField(loginEmailController, Icons.alternate_email, "Email Adress", false, TextInputType.emailAddress),
),
Container(
height: 40.0,
child: _createTextFormField(loginPasswordController, Icons.lock, "Password", true, TextInputType.text),
),
SizedBox(
width: double.infinity,
child: loginButton
)
],
),
);
}
Widget _createTextFormField(TextEditingController controller, IconData icon, String hintText, bool obscureText, TextInputType inputType){
return TextFormField(
keyboardType: inputType,
controller: controller,
obscureText: obscureText,
/* style: TextStyle(
fontSize: 15.0,
), */
decoration: InputDecoration(
/* contentPadding:
EdgeInsets.symmetric(vertical: 5.0, horizontal: 8.0), */
border: OutlineInputBorder(borderRadius: BorderRadius.circular(5.0)),
icon: Icon(
icon,
color: Colors.black,
size: 22.0,
),
//hintText: hintText,
labelText: hintText,
),
validator: (value) {
if (value.isEmpty) {
return 'Enter some text';
}
return null;
},
);
}
In your Code - you need to comment out the 40 height given to each container.
Container(
// height: 40.0,
child: _createTextFormField(
loginEmailController,
Icons.alternate_email,
"Email Adress",
false,
TextInputType.emailAddress),
),
Container(
// height: 40.0,
child: _createTextFormField(loginPasswordController, Icons.lock,
"Password", true, TextInputType.text),
),
and then in your - TextFormField in InputDecoration, you can alter these value as per your liking.
contentPadding:
EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
Above solutions did not work for me however I have figured out a very simple solution to avoid the above issue
TextFormField(
decoration: InputDecoration(
**errorStyle: const TextStyle(fontSize: 0.01),**
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(borderRadius),
borderSide: const BorderSide(
color: AppColor.neonRed,
width: LayoutConstants.dimen_1,
style: BorderStyle.solid,
),
),
);
Catch in the above solution is that we are setting the size of the error message to 0.01 so as a result it don't show up.
Additionally we can have custom border for the error.
Note : Setting the Text size to 0 is not working as it don't consider the text size and textFormField widget gets shrinked.
The problem is that we are not able to see your code so it might be challenging to assist you but I will do everything from scratch. You can firstly create the authentication class in one dart file
class AuthBloc{
StreamController _passController = new StreamController();
Stream get passStream => _passController.stream;
bool isValid(String pass){
_passController.sink.add("");
if(pass == null || pass.length < 6){
_passController.sink.addError("Password is too short");
return false;
}
else{
return true;
}
}
void dispose(){
_passController.close();
}
}
And then insert the following code in another dart file...
class LoginPage extends StatefulWidget{
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage>{
AuthBloc authBloc = new AuthBloc();
#override
void dispose(){
authBloc.dispose();
}
#override
Widget build(BuildContext context){
return Scaffold(
body: Container(
padding: EdgeInsets.fromLTRB(30, 0, 30, 0),
constraints: BoxConstraints.expand(),
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(0, 40, 0, 20),
child: StreamBuilder(
stream: authBloc.passStream,
builder: (context, snapshot) => TextField(
controller: _passController,
style: TextStyle(fontSize: 18, color: Colors.black),
decoration: InputDecoration(
errorText: snapshot.hasError ? snapshot.error:null,
labelText: "Password",
prefixIcon: Container(
width: 50,
child: Icon(Icons.lock),
),
border: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xffCED802), width: 1),
borderRadius: BorderRadius.all(Radius.circular(6))
)
),
),
)
),
Padding(
padding: const EdgeInsets.fromLTRB(0, 30, 0, 40),
child: SizedBox(
width: double.infinity,
height: 52,
child: RaisedButton(
onPressed: _onLoginClicked,
child: Text(
"Login",
style: TextStyle(fontSize: 18, color: Colors.white),
),
color: Color(0xff327708),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(6))
),
),
),
),
]
)
)
}
_onLoginClicked(){
var isValid = authBloc.isValid(_passController.text);
if(isValid){
//insert your action
}
}
}
I hope it works :)
Instead of using a fixed height container to wrap the textFormField, You can try to put a space in the helper text so it will keep the height of the field constant while only displaying when there is an error.
return TextFormField(
// ...
decoration: InputDecoration(
// ...
helperText: " ",
helperStyle: <Your errorStyle>,
)
According to Flutter Doc :
To create a field whose height is fixed regardless of whether or not an error is displayed, either wrap the TextFormField in a fixed height parent like SizedBox, or set the InputDecoration.helperText parameter to a space.
The problem with content padding is that you cant decrease the size of the field to UI requirement with an emphasize on decrease but how ever the second answer helped me come with a solution for my perticular problem, so am sharing that
StreamBuilder(
stream: viewModel.outEmailError,
builder: (context, snap) {
return Container(
width: MediaQuery.of(context).size.width*.7,
height: (snap.hasData)?55:35,
child: AccountTextFormField(
"E-mail",
textInputType: TextInputType.emailAddress,
focusNode: viewModel.emailFocus,
controller: viewModel.emailController,
errorText: snap.data,
textCapitalization: TextCapitalization.none,
onFieldSubmitted: (_) {
nextFocus(viewModel.emailFocus,
viewModel.passwordFocus, context);
},
),
);
}),