How to get static container background but scrollable loginform? - flutter

I want to scroll the loginform but NOT the background, but i noticed that the cointaner that contains these "circles" are moved to the up when keyboards appears, so i added
resizeToAvoidBottomInset: true,
To the scaffold, but now i cant scroll anything in the loginform and thats is what i dont want, i want to scroll ONLY the loggin form..
Here is the screenshot of the emulator, just click this
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:landscapes/bloc/inherited_provider.dart';
import 'package:landscapes/bloc/login_bloc.dart';
import 'package:landscapes/pages/register_page.dart';
class LoginPage extends StatefulWidget {
LoginPage({Key key}) : super(key: key);
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
body: Form(
key: formKey,
child: Stack(
children: <Widget>[
_crearFondo(),
_loginForm(context),
],
),
));
}
Widget _crearFondo() {
Size size = MediaQuery.of(context).size;
final fondoGris = Container(
height: size.height, width: size.width, color: Colors.grey[850]);
final circulo = Container(
width: 90.0,
height: 90.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100.0),
color: Color.fromRGBO(255, 255, 255, 0.05)),
);
return Stack(
children: <Widget>[
fondoGris,
Positioned(top: 50.0, left: 30.0, child: circulo),
Positioned(top: 150.0, right: 50.0, child: circulo),
Positioned(bottom: 180.0, right: 20.0, child: circulo),
Positioned(bottom: 280.0, left: 40.0, child: circulo),
],
);
}
Widget _loginForm(context) {
final bloc = InheritedProvider.loginBlocInherit(context);
final size = MediaQuery.of(context).size;
return SingleChildScrollView(
child: Column(
children: <Widget>[
SafeArea(child: Container(height: size.height * 0.05)),
SizedBox(height: size.height * 0.05),
welcomeBackForm(),
SizedBox(height: 55.0),
_formEmail(bloc),
SizedBox(height: 30.0),
_formPassword(),
SizedBox(height: 38),
_button(),
SizedBox(height: 50),
_crearCuenta(),
],
),
);
}
//MENSAJE BIENVENIDA
Widget welcomeBackForm() {
return Container(
alignment: Alignment.topLeft,
margin: EdgeInsets.only(left: 40.0),
child: Text(
'Hello!\nWelcome back',
style: GoogleFonts.playfairDisplay(
fontSize: 30, fontWeight: FontWeight.w600, color: Colors.white),
),
);
}
//TEXTFORMFIELD DEL EMAIL
Widget _formEmail(LoginBloc bloc) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 30.0),
child: TextFormField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
icon: Icon(
Icons.email,
color: Colors.white,
),
hintText: 'Email adress',
filled: true,
fillColor: Colors.grey[600],
enabledBorder: OutlineInputBorder(borderSide: BorderSide.none),
border: OutlineInputBorder(borderSide: BorderSide.none)),
style: TextStyle(height: 1),
validator: (value) {
Pattern pattern =
r'^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regExp = new RegExp(pattern);
if (regExp.hasMatch(value)) {
return null;
} else {
return ('El email no es correcto');
}
},
),
);
}
//TEXTFORMFIELD DEL PASSWORD
Widget _formPassword() {
return Container(
padding: EdgeInsets.symmetric(horizontal: 30.0),
child: TextFormField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
icon: Icon(
Icons.lock,
color: Colors.white,
),
hintText: 'Password',
filled: true,
fillColor: Colors.grey[600],
enabledBorder: OutlineInputBorder(borderSide: BorderSide.none),
border: OutlineInputBorder(borderSide: BorderSide.none),
),
style: TextStyle(height: 1),
),
);
}
//BOTON INGRESAR
Widget _button() {
return RaisedButton(
color: Color.fromRGBO(254, 200, 140, 1),
padding: EdgeInsets.symmetric(horizontal: 90.0, vertical: 15.0),
child: Text('ingresar'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
elevation: 0.0,
onPressed: () => _submit(),
);
}
//FLATBUTTON DE CREAR CUENTA
Widget _crearCuenta() {
return FlatButton(
onPressed: () => Navigator.pushReplacementNamed(context, 'registerpage'),
child: Text(
'Crear una nueva cuenta',
style: TextStyle(fontSize: 14),
),
textColor: Colors.white,
);
}
//SUBMIT DEL BOTON
void _submit() async {
if (!formKey.currentState.validate()) return null;
formKey.currentState.save();
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (BuildContext context) => RegisterPage()));
// Navigator.pushReplacementNamed(context, HomePage.routName).then((value) { setState(() { });});
}
}

You may try wrapping your _loginForm with SingleChildScrollView as follows :
SingleChildScrollView(child: _loginForm(context)),
By doing so the widgets within _crearFondo() will not scroll, however the widgets within _loginForm will scroll.
Please review the Flutter Documentation for SingleChildScrollView
SingleChildScrollView
A box in which a single widget can be scrolled.
This widget is useful when you have a single box that will normally be
entirely visible, for example a clock face in a time picker, but you
need to make sure it can be scrolled if the container gets too small
in one axis (the scroll direction).
It is also useful if you need to shrink-wrap in both axes (the main
scrolling direction as well as the cross axis), as one might see in a
dialog or pop-up menu. In that case, you might pair the
SingleChildScrollView with a ListBody child.
When you have a list of children and do not require cross-axis
shrink-wrapping behavior, for example a scrolling list that is always
the width of the screen, consider ListView, which is vastly more
efficient that a SingleChildScrollView containing a ListBody or Column
with many children.

Related

My animation gets reset to idle position, is there any workaround for rebuilding of widgets when soft keyboard pops up? - Flutter

I a beginner in Flutter development and I was trying to replicate an animated login screen from this LinkedIn post. I am using Rive animations to make all the states required for the screen possible. A base rive file was available for the screen and after some tweaks, I was able to get the required states.
After implementing the animations in the screen along with two text fields, I noticed an issue with the animations. Whenever I click on the TextField, the soft keyboard pops up. The required animation runs for a short duration then resets to the idle animation. After digging through stack overflow and google and printing debug messages, I noticed that the whole widget rebuilds itself whenever soft keyboard pops up.
Here's the code for my screen -
import 'package:flutter/material.dart';
import 'package:rive/rive.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({Key? key}) : super(key: key);
#override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
StateMachineController? _controller;
SMIInput<bool>? isChecking;
SMIInput<bool>? trigSuccess;
SMIInput<bool>? trigFail;
SMIInput<bool>? isHandsUp;
SMIInput<double>? numLook;
FocusNode emailFocusNode = FocusNode();
TextEditingController emailController = TextEditingController();
FocusNode passwordFocusNode = FocusNode();
TextEditingController passController = TextEditingController();
#override
void initState() {
super.initState();
emailFocusNode.addListener(emailFocus);
passwordFocusNode.addListener(passFocus);
}
#override
void dispose() {
super.dispose();
emailFocusNode.removeListener(emailFocus);
passwordFocusNode.removeListener(passFocus);
}
void emailFocus() {
isChecking?.change(emailFocusNode.hasFocus);
}
void passFocus() {
isHandsUp?.change(passwordFocusNode.hasFocus);
}
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: Color(0xffeef2f3),
// Colors.lightBlueAccent.withBlue(255).withGreen(242).withRed(238),
body: SafeArea(
child: SingleChildScrollView(
child: Container(
width: size.width,
height: size.height,
child: Center(
child: Container(
height: 600,
width: 320,
decoration: BoxDecoration(
color: Color(0xFFffffff),
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 5,
),
],
),
child: Padding(
padding: EdgeInsets.symmetric(
vertical: 25,
horizontal: 25,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
CircleAvatar(
backgroundColor: Colors.black87,
radius: 90,
child: CircleAvatar(
backgroundColor: Colors.transparent,
radius: 88,
child: ClipOval(
child: RiveAnimation.asset(
'assets/login_screen_character.riv',
stateMachines: ["Login Machine"],
onInit: (Artboard artboard) {
_controller =
StateMachineController.fromArtboard(
artboard, "Login Machine");
if (_controller == null) return;
artboard.addController(_controller!);
isChecking =
_controller?.findInput("isChecking");
trigSuccess =
_controller?.findInput("trigSuccess");
trigFail = _controller?.findInput("trigFail");
isHandsUp = _controller?.findInput("isHandsUp");
numLook = _controller?.findInput("numLook");
},
),
),
),
),
SizedBox(
height: 32,
),
Text(
"Email",
style: TextStyle(
fontWeight: FontWeight.bold,
color: Color(0xff32779d),
),
),
SizedBox(
height: 4,
),
Container(
decoration: BoxDecoration(
color: Color(0x321fb0d2),
borderRadius: BorderRadius.circular(5),
border: Border.all(
color: Color(0xff1fb0d2),
),
),
child: Padding(
padding: const EdgeInsets.only(
left: 16.0,
right: 16.0,
top: 8,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"email#example.com",
style: TextStyle(
fontSize: 12,
color: Color(0xff32779d),
),
),
TextField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
border: InputBorder.none,
hintText: "Enter email",
),
focusNode: emailFocusNode,
controller: emailController,
onChanged: (value) {
if (isChecking?.value == false)
isChecking?.change(true);
numLook?.change(value.length.toDouble() * 3);
},
),
],
),
),
),
SizedBox(
height: 32,
),
Text(
"Password",
style: TextStyle(
fontWeight: FontWeight.bold,
color: Color(0xff32779d),
),
),
SizedBox(
height: 2,
),
Container(
decoration: BoxDecoration(
color: Color(0x321fb0d2),
borderRadius: BorderRadius.circular(5),
border: Border.all(
color: Color(0xff1fb0d2),
),
),
child: Padding(
padding: const EdgeInsets.only(
left: 16.0,
right: 16.0,
top: 4,
bottom: 4,
),
child: TextField(
onChanged: (value) {
if (isHandsUp?.value == false)
isHandsUp?.change(true);
},
decoration: InputDecoration(
border: InputBorder.none,
hintText: "Enter password",
),
focusNode: passwordFocusNode,
controller: passController,
),
),
),
SizedBox(
height: 32,
),
ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Color(0xff1fb0d2),
),
),
onPressed: () {
if (isHandsUp?.value == true)
isHandsUp?.change(false);
Future.delayed(Duration(milliseconds: 1200), () {
if (passController.text == "thisisatest")
trigSuccess?.change(true);
else
trigFail?.change(true);
});
},
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
"Log In",
style: TextStyle(
color: Colors.white,
),
),
),
)
],
),
),
),
),
),
),
),
);
}
}
The code is a bit too long, please excuse me for that I haven't really refactored the code.
I've tried using the resizeToAvoidBottomInset: false but animation still resets to the idle state. I've even tried adding the android:windowSoftInputMode="adjustNothing" inside the <activity...> tag but still no result.
I would really like to know if there's any workaround in my code to make the animation states run properly.

How to show textinput box above the Keyboard while entering the input in flutter

I am using TextField widget in my project. I need to show the InputBox above the keyboard if the user clicks and start typing on the TextField, WhatsApp has this functionality in Landscape mode:
I need this functionality in my flutter application too, any little help will be very welcome, Thank you in advance.
Issue updated it here
below code will help you
import 'package:chat_system_flutter/utils/common_logic.dart';
import 'package:chat_system_flutter/utils/common_string.dart';
import 'package:flutter/material.dart';
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
import 'package:get/get.dart';
class ChatScreen extends StatefulWidget {
const ChatScreen({
Key? key,
}) : super(key: key);
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> with AutomaticKeepAliveClientMixin {
TextEditingController messageController = TextEditingController();
#override
void initState() {
super.initState();
}
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
return KeyboardVisibilityBuilder(
builder: (BuildContext context, bool isKeyboardVisible) {
if (isKeyboardVisible) {
// blok of code
}
return Obx(
() => Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
body: body(),
),
);
},
);
}
Widget body() {
return GestureDetector(
onTap: () {
hideKeyboard(context);
},
child: SafeArea(
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//message listview
],
),
),
),
Divider(
color: Theme.of(context).cardColor,
thickness: 2,
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 0,
),
child: Row(
children: [
Expanded(
child: TextField(
controller: messageController,
autocorrect: true,
enableSuggestions: true,
maxLines: 2,
onChanged: (val) {
// block of code
},
style: TextStyle(
fontSize: 16,
color: Theme.of(context).canvasColor,
),
decoration: InputDecoration(
hintText: typeYourMsgStr,
hintStyle: TextStyle(
fontSize: 16,
color: Theme.of(context).hintColor,
),
border: const UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.transparent,
),
),
enabledBorder: const UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.transparent,
),
),
focusedBorder: const UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.transparent,
),
),
),
textInputAction: TextInputAction.newline,
/* onSubmitted: (_) {
sendMsg();
},*/
),
),
GestureDetector(
onTap: () {
// send message
},
child: Container(
padding: const EdgeInsets.all(8),
child: Icon(
Icons.arrow_upward,
color: Theme.of(context).canvasColor,
),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(10)),
),
),
],
),
),
],
),
),
);
}
}
output
You need to add a padding in your main Container which contains your TextField like this:
padding: EdgeInsets.only(
top: 10,
right: 10,
left: 10,
bottom: MediaQuery.of(context).viewInsets.bottom + 10,
),
What MediaQuery.of(context).viewInsets.bottom does is that it takes the height of onscreen keyboard and adds it to padding whenever the keyboard appears on the screen. This makes your TextField to move 10 pixels above your keyboard (that's because we added 10 to bottom padding separately).
Another Widget that would help with scrolling is wrapping your main widget with a SingleChildScrollView.
Feel free to clear up any confusions in the comments :)

Flutter stack layout container use remaining height

I'm starting with Flutter.
I'm building a layout to look like
I'm using Stack to build this layout, where the part in background color is another Stack child with parent Container height to a fixed value.
The code I have written till now is
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:whatsappwithoutcontact/components/form_fields.dart';
import 'package:whatsappwithoutcontact/screens/info/info-screen.dart';
class MessageScreen extends StatefulWidget {
#override
_MessageScreenState createState() => _MessageScreenState();
}
class _MessageScreenState extends State<MessageScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('WhatsApp without Contact'),
backgroundColor: Colors.transparent,
elevation: 0.0,
actions: [
IconButton(
icon: Icon(Icons.info),
onPressed: _onInfoPressed,
)
],
),
extendBodyBehindAppBar: true,
body: MessageContainer()
);
}
void _onInfoPressed() {
Navigator.of(context).push(
MaterialPageRoute<void> (
builder: (BuildContext context) {
return InfoScreen();
}
)
);
}
}
class MessageContainer extends StatefulWidget {
#override
_MessageContainerState createState() => _MessageContainerState();
}
class _MessageContainerState extends State<MessageContainer> {
static const double avatarRadius = 35;
static const double titleBottomMargin = (avatarRadius * 2) + 18;
static const double _headerHeight = 350.0;
final _formKey = GlobalKey<FormState>();
final _messageFieldFocusNode = FocusNode();
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Stack(
children: [
Container(
height: _headerHeight,
child: Stack(
children: <Widget>[
Container(
child: ClipPath(
clipper: HeaderClipper(avatarRadius: avatarRadius),
child: CustomPaint(
size: Size(MediaQuery.of(context).size.width, _headerHeight),
painter: HeaderPainter(
color: Colors.green,
avatarRadius: avatarRadius
),
),
),
),
Positioned(
left: 0,
right: 0,
child: Container(
padding: EdgeInsets.only(top: 80.0),
margin: EdgeInsets.all(50.0),
child: Column(
children: <Widget>[
FormTextField(
labelText: 'Country Code',
validator: (value) {
if (value.isEmpty) {
return 'Required';
}
return null;
}
),
SizedBox(height: 15.0,),
FormTextField(
labelText: 'Phone Number',
validator: (value) {
if (value.isEmpty) {
return 'Required';
}
return null;
},
)
],
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: CircleAvatar(
radius: avatarRadius,
backgroundColor: Colors.green,
child: IconButton(icon: Icon(Icons.message), onPressed: _onAddMessageButtonClick,),
),
)
],
),
),
SingleChildScrollView(
child: Padding(
padding: EdgeInsets.symmetric(
vertical: _headerHeight,
horizontal: 50
),
child: Column(
children: [
TextFormField(
keyboardType: TextInputType.multiline,
maxLines: 4,
focusNode: _messageFieldFocusNode,
decoration: InputDecoration(
labelText: 'Message',
fillColor: Colors.white,
labelStyle: TextStyle(
color: _messageFieldFocusNode.hasFocus ? Colors.green : Colors.grey
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey
)
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.green
)
)
),
)
],
),
),
),
],
)
);
}
}
The first Container inside the first Stack is the green background part and SingleChildScrollView is the part below the green color background.
The SingleChildScrollView is making the lower part scrollable whereas I want it to be fixed and take up remaining space below the message icon in the center.
I tried using Container but then the Message input field is not displayed.
How can I use the remaining space below the Stack layout?
Is my layout structure good according to the design. I need suggestions on how to improve it, if not good.
Replace the first Stack widget with Column, now you can wrap your SingleChildScrollView widget into an Expanded widget, the Expanded widget works only inside Column and Row to fill the available space.
Don't forget to remove vertical padding from the Message input field.

Widgets shifted down when error shows for TextFormField on button's press

I have created a login page in Flutter. I have used SingleChildScrollView to make this page scrollable.
I have used the form validation to validate the username and password text form fields.
The problem is when error messages displayed below TextFormField, the login button is shifted down.
I have also used ListView to enable the scrolling but the behavior is the same as far as shifting is concerned.
I am new to Flutter and seeking help in this.
I am providing the code and screenshot below.
Thank you.
Screenshot
Login Page
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'login_form.dart';
class LoginPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(child: LoginForm()),
);
}
}
Login Form
import 'package:flutter/material.dart';
class LoginForm extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _LoginFormState();
}
}
class _LoginFormState extends State<LoginForm> {
TextStyle style = TextStyle(fontFamily: 'Montserrat', fontSize: 20.0);
OutlineInputBorder textFieldBorder = OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0),
borderSide: BorderSide(color: const Color(0xff01d277)));
final formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
/// Username field
final usernameField = TextFormField(
obscureText: false,
style: style,
validator: (value) {
if (value.isEmpty) return 'Please enter username';
return null;
},
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
hintText: 'Username',
border: textFieldBorder,
));
/// Password field
final passwordField = TextFormField(
obscureText: true,
style: style,
validator: (value) {
if (value.isEmpty) return 'Please enter password';
return null;
},
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
hintText: 'Password',
border: textFieldBorder,
));
/// Login button
final loginButton = Material(
elevation: 5.0,
borderRadius: BorderRadius.circular(30.0),
color: Color(0xff01d277),
child: MaterialButton(
minWidth: MediaQuery.of(context).size.width,
padding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
onPressed: () {
if (formKey.currentState.validate())
Scaffold.of(context).showSnackBar(SnackBar(content: Text('Done!')));
},
child: Text("Login",
textAlign: TextAlign.center,
style: style.copyWith(
color: Colors.white, fontWeight: FontWeight.bold)),
),
);
return Padding(
padding: const EdgeInsets.all(36.0),
child: Form(
key: formKey,
child: Column(
children: <Widget>[
SizedBox(
height: 150,
child: Image.asset(
'lib/assets/images/logo_dark.png',
fit: BoxFit.contain,
),
),
SizedBox(height: 45.0),
usernameField,
SizedBox(
height: 45.0,
),
passwordField,
SizedBox(
height: 45.0,
),
loginButton,
],
)));
}
}
Use ConstrainedBox with fixed maxHeight and minHeight
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: 70.0,
minHeight: 70.0,
child: TextFormField(...),
),
);
Use 'Container' widget to set full height.
Container(
width: double.infinity,
height: 73, //(text field height + error label height)
child: TextFormField(<your Text Form Field>)
)
Try wrapping the usernameField and passwordField in an Expanded widget
Expanded(child: usernameField),
SizedBox(
height: 45.0,
),
Expanded(child: passwordField),

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);
},
),
);
}),