im trying to put the Textvalue, i have created via Texteditingcontroller into the text on the Neuigkeiten Page via controller.value, so the texteditingvalue gets displayed on the other page, but my texteditingcontroller do not get recognised in the neugkeitenpage and i cant edit anything there at all.
class InformationContentDetails extends StatefulWidget {
const InformationContentDetails({Key key}) : super(key: key);
#override
State<InformationContentDetails> createState() => _InformationContentDetails();
}
class _InformationContentDetails extends State<InformationContentDetails> {
bool isEnable = false;
var _controller = new TextEditingController(text: 'Allgemeine Informationen');
var _controller2 = TextEditingController();
String name = "Allgemeine Informationen";
String name2 = "Herzlich Willkommen ...";
textlistener(){
print("Update: ${_controller.text}");
print("Update: ${_controller2.text}");
}
#override
void initState() {
super.initState();
// Start listening to changes
_controller.addListener(textlistener);
_controller2.addListener(textlistener);
}
#override
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInformation) {
var textAlignment;
if (sizingInformation.deviceScreenType == DeviceScreenType.desktop) {
textAlignment = TextAlign.left;
} else {
textAlignment = TextAlign.center;
}
return Container(
width: 650,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"${_controller.text}",
style: titleTextStyle(sizingInformation.deviceScreenType),
textAlign: textAlignment,
),
TextField(
enabled: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Informationen aktualisieren",
),
controller : _controller,
),
FlatButton(
child: Text('bearbeiten'),
onPressed:(){
setState((){
name = _controller.text;
isEnable = !isEnable;
});
},
),
Text(
name2,
style: descriptionTextStyle(sizingInformation.deviceScreenType),
textAlign: textAlignment,
),
Container(
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Informationstext aktualisieren"
),
controller : _controller2,
),
),
Container(
child: FlatButton(
child: Text('bearbeiten'),
onPressed:(){
setState((){
name2 = _controller2.text;
isEnable = !isEnable;
});
},
),
),
],
)),
);
},
);
}
#override
void dispose() {
_controller.dispose();
_controller2.dispose();
super.dispose();
}
}
class NeuigkeitenContentDetails extends StatefulWidget {
const NeuigkeitenContentDetails({Key key}) : super(key: key);
#override
State<NeuigkeitenContentDetails> createState() => _NeuigkeitenContentDetailsState();
}
class _NeuigkeitenContentDetailsState extends State<NeuigkeitenContentDetails> {
#override
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInformation) {
var textAlignment;
if (sizingInformation.deviceScreenType == DeviceScreenType.desktop) {
textAlignment = TextAlign.left;
} else {
textAlignment = TextAlign.center;
}
return Container(
width: 650,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Neuigkeiten',
style: titleTextStyle(sizingInformation.deviceScreenType),
textAlign: textAlignment,
),
SizedBox(
height: 30,
),
Text(
'Herzlich Willkommen',
style: descriptionTextStyle(sizingInformation.deviceScreenType),
textAlign: textAlignment,
)
],
),
);
},
);
}
}
It isn't clear from your code what you mean by pages.
How does InformationContentDetails relate to, or interact with, NeuigkeitenContentDetails?
If you are using e.g. named routes you may want to look at e.g. Pass arguments to a named route and or Send data to a new screen, if on the other hand you need to send information between different widgets in the widget tree you may need to look at something like Provider - allowing you to read-only or watch for change and rebuild subscribed code, or other state management solutions.
Otherwise, if they exist simultaneously in the widget tree and you don't want to use a dedicated state management solution you need to pass the necessary data up-and-down through parameters which can get messy. H.t.h.
Related
I'm trying to create a pageview that I can load a widget into that is defined in another file. This works fine, except when I try to add a callback, I get the following error:
FlutterError (setState() or markNeedsBuild() called during build.
This error is triggered when the email entered is considered to be valid (that is, when the code in the email_entry.dart calls the callback function that was passed from the account_onboarding.dart file.) I haven't been able to determine why this is happening, and no tutorials on this subject seem to exist. I am still pretty new to Dart/Flutter, so I'm hoping someone can point out what's happening (and a fix) here.
Here is my code:
-Parent widget, account_onboarding.dart
import 'package:flutter/material.dart';
import 'package:page_view_indicators/page_view_indicators.dart';
import 'package:animated_title_screen/screens/email_entry.dart';
class AccountOnboarding extends StatefulWidget {
const AccountOnboarding({Key? key}) : super(key: key);
#override
State<AccountOnboarding> createState() => _AccountOnboardingState();
}
class _AccountOnboardingState extends State<AccountOnboarding> {
final _pController = PageController(initialPage: 0);
final _currentPageNotifier = ValueNotifier<int>(0);
final List<Widget> _pages = [];
bool validEmail = false; //Callback should set this variable
#override
void initState() {
super.initState();
_pages.add( //Add the EmailEntry widget to the list
EmailEntry(emailIsValid: (p0) {
setState(() {
validEmail = p0;
});
},),
);
_pages.add(
Container(
color: Colors.blue,
child: Text("Pg2"),
),
);
_pages.add(
Container(
color: Colors.green,
child: Text("Pg3"),
),
);
}
#override
void dispose() {
_pController.dispose();
_currentPageNotifier.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Create Account",
style: Theme.of(context).textTheme.headline5,
),
centerTitle: true,
actions: [
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
],
),
body: Stack(
fit: StackFit.expand,
children: [
Column(
children: [
Row(
children: [
Text(
"Step ${_currentPageNotifier.value + 1} of ${_pages.length}",
),
CirclePageIndicator(
dotColor: const Color(0xFF323232),
selectedDotColor: const Color(0xFFE4231F),
size: 10,
selectedSize: 10,
currentPageNotifier: _currentPageNotifier,
itemCount: _pages.length,
),
],
),
PageView(
controller: _pController,
onPageChanged: (index) {
setState(() {
_currentPageNotifier.value = index;
});
},
children: [
for (Widget p in _pages) p, //Display all pages in _pages[]
],
),
ElevatedButton(
child: const Text("Continue"),
onPressed: () => print("Pressed 2"),
style: ElevatedButton.styleFrom(
primary: validEmail ? const Color(0xFFE1251B) : Colors.black,
textStyle: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
],
),
],
),
);
}
}
Here is the email_entry.dart code:
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class EmailEntry extends StatefulWidget {
final Function(bool) emailIsValid; //Function required in constructor
const EmailEntry({Key? key, required this.emailIsValid}) : super(key: key);
#override
State<EmailEntry> createState() => _EmailEntryState();
}
class _EmailEntryState extends State<EmailEntry> {
final _emailController = TextEditingController();
FocusNode _emailFocus = FocusNode();
#override
void initState() {
super.initState();
_emailController.addListener(() => setState(() {}));
_emailFocus.addListener(() {
print("Focus email");
});
}
#override
void dispose() {
_emailController.dispose();
super.dispose();
}
bool validateEmail(String email) {
bool valid = RegExp(
r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+#[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*$")
.hasMatch(email);
if (valid) {
widget.emailIsValid(true); //Call the callback function
}
return valid;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Column(
children: [
Text(
"YOUR EMAIL",
style: Theme.of(context).textTheme.headline2,
),
Text(
"Please use an email address that you would like to make your login.",
style: Theme.of(context).textTheme.bodyText2,
textAlign: TextAlign.center,
),
Container(
child: Text(
"Email Address",
),
),
TextField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
focusNode: _emailFocus,
suffixIcon: getTextFieldSuffix(emailController, _emailFocus), //OFFENDING CODE
),
],
),
],
),
);
}
//THIS FUNCTION CAUSED THE ISSUE. It is code I got from a youtube //tutorial. Probably should have guessed.
Widget getTextFieldSuffix(TextEditingController controller, FocusNode node) {
if (controller.text.isNotEmpty && node.hasFocus) {
return IconButton(
color: Colors.grey.withAlpha(150),
onPressed: () => controller.clear(),
icon: const Icon(Icons.close));
} else if (controller.text.isNotEmpty && !node.hasFocus) {
return const Icon(
Icons.check,
color: Colors.green,
);
}
return Container(
width: 0,
);
}
}
in initState,you need to call addPostFrameCallback.
like this...
#override
void initState() {
super.initState();
///Add This Line
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
///All of your code
});
}
I found out that there is some code in my production version that calls a redraw every time the user enters a letter into the textfield for the email address. This was causing a problem because the screen was already being redrawn, and I was calling setState to redraw it again. I will edit the code shown above to include the offending code.
I'm using responsive_sizer package for my app..
my whole tree is rebuilt when the keyboard opens with a textfield.
Here the code of the textfield :
class ProfileNameTextField extends StatefulWidget {
const ProfileNameTextField({Key? key}) : super(key: key);
#override
_ProfileNameTextFieldState createState() => _ProfileNameTextFieldState();
}
class _ProfileNameTextFieldState extends State<ProfileNameTextField> {
TextEditingController? _controller;
String _previousName = "";
FocusNode? _focusNode;
final String _forbiddenCharacters = "1234567890&)°(+=/,;.£\$*€<>\_##";
Widget _subText = Container();
#override
void initState() {
// TODO: implement initState
_controller = TextEditingController();
_previousName = CloudUser.instance.username;
_controller!.text = CloudUser.instance.username;
_focusNode = FocusNode();
_focusNode!.addListener(() {
if(!_focusNode!.hasFocus) {
print("Focus on name textfield is lost");
_onSubmitted(_controller!.text);
}
});
super.initState();
}
#override
void dispose() {
// Clean up the focus node when the Form is disposed.
_focusNode!.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
Widget? _suffix;
switch(Provider.of<LoadingProvider>(context).state) {
case LoadingState.busy:
_suffix = SpinKitRing(
color: Theme
.of(context)
.primaryColor,
lineWidth: 2,
size: Theme.of(context).textTheme.subtitle1!.fontSize!
);
break;
case LoadingState.idle:
_suffix = Container();
break;
}
return CustomTextContainer(
child: InkWell(
onTap: _giveFocus,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children:
[
Text(
"Prénom",
style: Theme.of(context).textTheme.bodyText2!.copyWith(
fontSize: Theme.of(context).textTheme.bodyText2!.fontSize!.sp
)
),
Container(height: Sizer().heightSmallSpace),
Container(height: Theme.of(context).textTheme.bodyText1!.fontSize,
child: Row(children: [
Expanded(
child: TextField(
keyboardType: TextInputType.name,
controller: _controller,
onSubmitted: _onSubmitted,
focusNode: _focusNode,
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.zero,
),
style: Theme.of(context).textTheme.bodyText1!.copyWith(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.w600,
fontSize: Theme.of(context).textTheme.bodyText1!.fontSize!.sp
),
textAlign: TextAlign.start,
),),
_suffix,
]),
),
Container(height: Sizer().heightSmallSpace),
Row(children: [
Spacer(),
Container(
height: Theme.of(context).textTheme.subtitle1!.fontSize!*1.2,
child: Center(child: _subText)),
]),
Container(height: Sizer().heightSmallSpace),
]
)
)
);
}
_onSubmitted(String username) {
RegExp regExp = RegExp('[' + _forbiddenCharacters + ']');
if(!regExp.hasMatch(username)) {
if(_previousName != username) {
print("name is " + username);
_previousName = username;
setState(() {
_subText = Container();
});
Provider.of<LoadingProvider>(context, listen: false).update('username', username).then((result) {
if(result) {
CloudUser.instance.username = username;
setState(() {
_subText = Text(
"Enregistré",
style: Theme
.of(context)
.textTheme
.subtitle1!
.copyWith(
color: color.success,
fontSize: Theme.of(context).textTheme.subtitle1!.fontSize!.sp
),
);
});
}
else
setState(() {
_subText = Text(
"Erreur serveur",
style: Theme.of(context).textTheme.subtitle1!.copyWith(
color: Theme.of(context).errorColor,
fontSize: Theme.of(context).textTheme.subtitle1!.fontSize!.sp
),
);
});
});
}
} else {
setState(() {
_subText = Text(
"Caractères interdits",
style: Theme.of(context).textTheme.subtitle1!.copyWith(
color: Theme.of(context).errorColor,
fontSize: Theme.of(context).textTheme.subtitle1!.fontSize!.sp
),
textAlign: TextAlign.right,
);
});
}
}
_giveFocus() {
_focusNode!.requestFocus();
}
}
Within Sizer(), i have :
double padding = 2.h;
double widgetHeight = 8.h;
double iconButton = 4.h;
double radius = 15;
double lineWidth = 3.h;
double heightSpace = 3.h;
double heightSmallSpace = 0.9.h;
double gridSpacing = 0.3.h;
double widthSpace = 1.25.w;
ProfileNameTextField is included in
class _ProfileControllerState extends State<ProfileController> {
#override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).backgroundColor,
child: Column(
children: [
ProfileAppBar(
onSetting: _onSetting,
),
Flexible(
child: Container(
padding: EdgeInsets.symmetric(horizontal: Sizer().padding/3),
color: Theme.of(context).scaffoldBackgroundColor,
child: Scrollbar(
child: SingleChildScrollView(
physics: ClampingScrollPhysics(),
child: Container(
padding: EdgeInsets.symmetric(horizontal: Sizer().padding*2/3),
child: Column(children:
[
Container(height: Sizer().heightSpace),
SvgPicture.asset(
"assets/icons/phone_kisses.svg",
height: Sizer().widgetHeight*3,
width: Sizer().getCustomWidth(66),
fit: BoxFit.contain,
),
_space(),
ChangeNotifierProvider<LoadingProvider>(
create: (BuildContext context) => LoadingProvider(),
child: ProfileNameTextField(),
),
I have this problem since the import of the responsive_sizer... I do not understand where the problem can come from.
I tried resizetoavoidbottominset but nothing changed.
I found the problem. It's not coming from MediaQuery, but from the Responsive_sizer package.
When I open the keyboard, I actually update the height and width. But this package must necessarily encompass your MaterialApp in the following way:
MaterialApp(
home: ResponsiveSizer(
builder: (context, orientation, screenType) {
return const HomePage();
},
),
);
And that's the problem. Under my homepage, I have a stream to see if the user is logged in, which then leads to the profile page, among other things. I don't want it to reload, I just want the profile to reload.
My solution: I use MediaQuery in a similar way to Responsive_sizer.... instead of using the .h and .w package, I use MediaQuery.of(context).size.height and its counterpart. The same thing for the font size.
I hope this can help those who have the same problem as me on this package,
good evening.
I am really struggling to understand why my code isn't working. I'm trying to pass the text from two controllers into another widget with named parameters which writes to Firebase.
My "Test" button properly prints both _titleController.text and _descriptionController.text
TextButton(
onPressed: (){
print(_titleController.text); //works fine
print(_descriptionController.text); //works fine
},
child: Text('test')
),
However when I pass these into my next widget it's blank! If I hardcore strings into these parameters it works properly:
PushNewE3 (
changeTitle: _titleController.text, //does not work (empty)
changeDescription: _descriptionController.text, //does not work (empty)
)
Full code:
class CreateE3 extends StatefulWidget {
const CreateE3({Key? key}) : super(key: key);
#override
_CreateE3State createState() => _CreateE3State();
}
class _CreateE3State extends State<CreateE3> {
final _titleController = TextEditingController();
final _descriptionController = TextEditingController();
#override
void initState(){
super.initState();
_titleController.addListener(_printLatestValue);
}
#override
void dispose(){
_titleController.dispose();
super.dispose();
}
void _printLatestValue(){
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('So Frustrating'),
),
body: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 800,
child: Column(
children: [
Text('Originator: **Add Current User**') ,
TextField(
maxLength: 40,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Change Title'
),
controller: _titleController,
onEditingComplete: (){
//_title = _titleController.text;
},
),
Padding(
padding: const EdgeInsets.fromLTRB(0,10,0,0),
child: TextFormField(
maxLines: 5,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Detailed Description'
),
controller: _descriptionController,
),
),
TextButton(
onPressed: (){
print(_titleController.text); //successfully prints
print(_descriptionController.text); //successfully prints
},
child: Text('test')
),
PushNewE3 (
changeTitle: _titleController.text, //DOES NOT WORK (empty)
changeDescription: _descriptionController.text, //DOES NOT WORK (empty)
)
],
),
),
],
),
);
}
}
class PushNewE3 extends StatelessWidget {
final String changeTitle;
final String changeDescription;
PushNewE3({
required this.changeTitle,
required this.changeDescription
});
#override
Widget build(BuildContext context) {
// Create a CollectionReference called users that references the firestore collection
CollectionReference notificationsE3 = FirebaseFirestore.instance.collection('notificationsE3');
Future<void> pushNewE3() {
// Call the notifications CollectionReference to add a new E3 notification
return notificationsE3
.add({
//'originator': FirebaseAuth.instance.currentUser,
'changeTitle': changeTitle,
'changeDescription': changeDescription,
})
.then((value) => print("E3 Created"))
.catchError((error) => print("Failed to create E3: $error"));
}
return TextButton(
onPressed: (){
print('start:');
print(changeTitle);
print(changeDescription);
print('-end');
},
child: Text(
"Create E3",
),
);
}
}
EDIT:
I still don't understand why the above code doesn't work. I refactored my code into a single widget and now it's working. If anyone can explain why I would still appreciate understanding as there is clearly a gap in my knowledge.
If anyone in the future runs into the same problem here is the refactored code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'main.dart';
var global = 'blank';
class CreateE3 extends StatefulWidget {
const CreateE3({Key? key}) : super(key: key);
#override
_CreateE3State createState() => _CreateE3State();
}
class _CreateE3State extends State<CreateE3> {
final _titleController = TextEditingController();
final _descriptionController = TextEditingController();
#override
Widget build(BuildContext context) {
// Create a CollectionReference called users that references the firestore collection
CollectionReference notificationsE3 = FirebaseFirestore.instance.collection('notificationsE3');
Future<void> pushNewE3() {
// Call the notifications CollectionReference to add a new E3 notification
return notificationsE3
.add({
//'originator': FirebaseAuth.instance.currentUser,
'changeTitle': _titleController.text,
'changeDescription': _descriptionController.text,
})
.then((value) => print("E3 Created"))
.catchError((error) => print("Failed to create E3: $error"));
}
return Scaffold(
appBar: AppBar(
title: Text(_titleController.text),
),
body: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 800,
child: Column(
children: [
Text('Originator: **Add Current User**') ,
TextField(
maxLength: 40,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Change Title'
),
controller: _titleController,
onChanged: (text){
setState(() {
});
},
),
Padding(
padding: const EdgeInsets.fromLTRB(0,10,0,0),
child: TextFormField(
maxLines: 5,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Detailed Description'
),
controller: _descriptionController,
),
),
TextButton(
onPressed: (){
pushNewE3();
},
child: Text('SAVE')
),
],
),
),
],
),
);
}
}
in the onPressed To pass a value and show it, you have to use setState(() { _myState = newValue; });
Something like this
TextButton(
onPressed: (){
print(_titleController.text);
print(_descriptionController.text);
setState(() { _myNewText = _titleController.text; });
},
child: Text('test')
),
I'm not sure what are you trying to do exactly but here's what I did:
1 - add a local variable _title
2 - add this code to the onPressed function:
setState(() {
_title= _titleController.text;
});
This is the whole code :
class CreateE3 extends StatefulWidget {
const CreateE3({Key? key}) : super(key: key);
#override
_CreateE3State createState() => _CreateE3State();
}
class _CreateE3State extends State<CreateE3> {
final _titleController = TextEditingController();
final _descriptionController = TextEditingController();
String _title = 'So Frustrating';
#override
void initState(){
super.initState();
}
#override
void dispose(){
_titleController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_title),
),
body: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 400,
child: Column(
children: [
Text('Originator: **Add Current User**') ,
TextField(
maxLength: 40,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Change Title'
),
controller: _titleController,
onEditingComplete: (){
//_title = _titleController.text;
},
),
Padding(
padding: const EdgeInsets.fromLTRB(0,10,0,0),
child: TextFormField(
maxLines: 5,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Detailed Description'
),
controller: _descriptionController,
),
),
TextButton(
onPressed: (){
print(_titleController.text);
print(_descriptionController.text);
setState(() {
_title = _titleController.text;
});
},
child: Text('test')
),
],
),
),
],
),
);
}
}
.........................
so this is when you first start the app :
after changing the TextField and pressing the 'test button the title in the appbar change :
When I click on show/hide toggle, both password and username textfield values gets deleted. I understand that, when setState rebuilds the widget tree, it resets password/username textfield values to initial blank values. I know I can get value of textfield, However I am not able to implement logic, how can I first get current username and password value, save it somewhere and then insert it back at the time widget rebuilds alongwith setState.
import 'package:flutter/material.dart';
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
bool _showPassword = true;
#override
Widget build(BuildContext context) {
final usernameController = TextEditingController();
final passwordController = TextEditingController();
return SafeArea(
child: Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image(image: AssetImage('images/logo.png')),
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: usernameController,
decoration: InputDecoration(
hintText: "Enter Username",
labelText: "UserName",
),
),
SizedBox(
height: 20.0,
),
TextField(
obscureText: _showPassword,
controller: passwordController,
decoration: InputDecoration(
hintText: "Enter Password",
labelText: "Password",
suffixIcon: GestureDetector(
onTap: _togglePasswordVisibility,
child: _showPassword
? Icon(Icons.visibility)
: Icon(Icons.visibility_off),
),
),
),
SizedBox(
height: 20.0,
),
RaisedButton(
onPressed: () {
//call api here to authenticate user
},
child: Text("Login"),
),
],
),
),
Column(
children: [
Text("Don't have an account! Get it in 2 easy steps"),
RaisedButton(
onPressed: () {
//take user to registration screen
},
child: Text("Register"),
),
],
),
],
),
),
),
);
}
void _togglePasswordVisibility() {
setState(() {
_showPassword = !_showPassword;
});
}
}
When you use the setState to toggle the visibility, the build method will get called to redraw that widget. Since you are initialising your TextEditingController in the build method, it get initialised again and loose the previous value. To fix this you just remove the initialisation from build method to class level.
class _LoginScreenState extends State<LoginScreen> {
bool _showPassword = true;
final usernameController = TextEditingController();
final passwordController = TextEditingController();
#override
Widget build(BuildContext context) {
return SafeArea(
//... Your code
);
}
//... Your code
}
I am trying to build a dynamic Authentication Screen i.e I have a form which shows fields depending on whether the form isLoginForm or not.
I tried initialising the CustomForm class in the State of my LoginScreen but everytime I hot reload or restart, the app says that the column's child is null i.e. the CustomForm is null.
I initialized the form in the void initState() because it also takes textControllers as one of its parameters in the constructor.
Here is the code for LoginScreen class:
class LoginScreen extends StatefulWidget {
LoginScreen({Key key}) : super(key: key);
TextEditingController _nameController;
TextEditingController _emailController;
TextEditingController _passwordController;
CustomForm _form;
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
final _formKey = GlobalKey<FormState>();
#override
void initState() {
widget._nameController = TextEditingController();
widget._emailController = TextEditingController();
widget._passwordController = TextEditingController();
initForm();
widget._nameController.addListener(_nameControllerListener);
widget._emailController.addListener(_emailControllerListener);
widget._passwordController.addListener(_passwordControllerListener);
super.initState();
}
void initForm() {
widget._form = CustomForm(
isLoginForm: false,
nameController: widget._nameController,
passwordController: widget._passwordController,
emailController: widget._emailController,
);
}
#override
void dispose() {
widget._nameController.removeListener(_nameControllerListener);
widget._nameController.dispose();
widget._emailController.removeListener(_emailControllerListener);
widget._emailController.dispose();
widget._passwordController.removeListener(_passwordControllerListener);
widget._passwordController.dispose();
super.dispose();
}
void _nameControllerListener() {
print("(_nameValue Call)__name value is: ${widget._nameController.text}\n");
}
void _emailControllerListener() {
print(
"(_emailValue Call)__email value is: ${widget._emailController.text}\n");
}
void _passwordControllerListener() {
print(
"(_passwordValue Call)__password value is: ${widget._passwordController.text}\n");
}
#override
Widget build(BuildContext context) {
ScreenUtil.init();
return Scaffold(
body: Container(
height: double.infinity,
width: double.infinity,
child: SafeArea(
child: SingleChildScrollView(
child: SizedBox(
height: 1920.h - MediaQuery.of(context).padding.top,
child: Stack(children: <Widget>[
SvgPicture.asset('assets/svg/login/top_left.svg',
width: 320.w),
Positioned(
bottom: 0,
right: 0,
child: SvgPicture.asset(
'assets/svg/login/bottom_right.svg',
width: 320.w)),
Center(
child: Container(
width: 793.w,
child: Column(children: <Widget>[
SizedBox(height: 223.h),
Text('Login',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 100.sp,
fontFamily: 'Avenir')),
SizedBox(height: 60.h),
SvgPicture.asset('assets/svg/login/main_image.svg',
width: 266.w),
SizedBox(height: 143.h),
widget._form,
])),
)
]),
),
),
)));
}
}
And here is the code for the CustomForm class:
class CustomForm extends StatefulWidget {
CustomForm(
{Key key,
this.isLoginForm,
this.nameController,
this.emailController,
this.passwordController})
: super(key: key);
bool isLoginForm;
final nameController;
final emailController;
final passwordController;
#override
_CustomFormState createState() => _CustomFormState();
}
class _CustomFormState extends State<CustomForm> {
CustomTextFormField _name;
CustomTextFormField _email;
CustomTextFormField _password;
final authBloc = AuthBloc();
#override
void initState() {
_name = CustomTextFormField(
hint: "Your Name",
isPassword: false,
isLoginForm: widget.isLoginForm,
keyboardType: TextInputType.emailAddress,
textController: widget.nameController,
stream: authBloc.nameStream,
onChanged: authBloc.nameSink,
);
_email = CustomTextFormField(
hint: "Your Email",
isPassword: false,
isLoginForm: widget.isLoginForm,
keyboardType: TextInputType.emailAddress,
textController: widget.emailController,
stream: authBloc.emailStream,
onChanged: authBloc.emailSink,
);
_password = CustomTextFormField(
hint: "Password",
isPassword: true,
isLoginForm: widget.isLoginForm,
keyboardType: TextInputType.visiblePassword,
textController: widget.passwordController,
stream: authBloc.passwordStream,
onChanged: authBloc.passwordSink,
);
super.initState();
}
#override
Widget build(BuildContext context) {
ScreenUtil.init();
double rectangleGap1h = widget.isLoginForm ? 20.h : 10.h;
double rectangleGap2h = widget.isLoginForm ? 23.h : 14.h;
double btnHeight = widget.isLoginForm ? 133.h : 120.h;
String submitBtnText = widget.isLoginForm ? "Login" : "Sign Up";
String redirectText = widget.isLoginForm ? "Sign Up" : "Login";
String bottomQuestionText = widget.isLoginForm
? "Don\'t have an account?"
: "Already have an account?";
int _defaultRadioVal = -1;
return Form(
child: Column(
children: <Widget>[
Visibility(
visible: !widget.isLoginForm,
child: Column(
children: <Widget>[
//RADIO GROUP
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Radio(
value: 0,
groupValue: _defaultRadioVal,
onChanged: (int i) {}),
Text('Customer'),
Radio(
value: 0,
groupValue: _defaultRadioVal,
onChanged: (int i) {}),
Text('Designer'),
],
),
SizedBox(height: rectangleGap1h),
_name,
SizedBox(height: rectangleGap1h),
],
),
),
_email,
SizedBox(
height: rectangleGap1h,
),
_password,
SizedBox(height: rectangleGap1h),
_buildSubmitButton(submitBtnText, btnHeight),
SizedBox(
height: rectangleGap2h,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
SizedBox(width: 71.w),
Text(
bottomQuestionText,
style: TextStyle(
color: kHintTextColor,
fontSize: 43.sp,
fontFamily: 'Avenir',
),
),
Text(
redirectText,
style: TextStyle(
color: kPrimaryColor,
fontSize: 43.sp,
fontFamily: 'Avenir',
fontWeight: FontWeight.w700,
),
),
SizedBox(width: 71.w),
// Visibility(
// visible: !widget.isLoginForm,
// child: _buildBottomSignUpContainer()),
],
)
],
),
);
}
Every time I rerun the app it seems to work but on hot reload and restart it doesn't work.
Will this work otherwise or will this thing cause an error later as well?
Is this just because on hot reload the initState() method is not called?
Please help
Thank you :)