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.
Related
I am making a quiz app and at first everything works fine, but when I do a quiz the first time, it does the correct or incorrect answer check perfectly.
But when I go back to quiz without restarting the app just navigating from one page to another the PageView does not reset its state again.
Before taking the quiz
enter image description here
After I do the quiz and I want to do it again without restart the app, I get the checked answers.
enter image description here
How to return the PageView to its initial state without restart the app
Here is my code:
import 'package:flutter/material.dart';
import 'package:quizapp/src/models/quiz_model.dart';
import 'package:quizapp/src/screens/result_screen.dart';
class QuizScreen extends StatefulWidget {
const QuizScreen({Key? key}) : super(key: key);
#override
State<QuizScreen> createState() => _QuizScreenState();
}
class _QuizScreenState extends State<QuizScreen> {
int _questionNumber = 1;
late PageController _controller;
int _score = 0;
#override
void initState() {
_controller = PageController(initialPage: 0);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: PageView.builder(
physics: const NeverScrollableScrollPhysics(),
controller: _controller,
itemCount: questions.length,
itemBuilder: (context, index) {
final _question = questions[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 16,
),
Text(
_question.text,
style: const TextStyle(fontSize: 22),
),
const SizedBox(
height: 16,
),
Expanded(
child: SingleChildScrollView(
child: Column(
children: _question.options
.map((option) => GestureDetector(
onTap: () {
Future.delayed(
const Duration(milliseconds: 250),
() {
if (_questionNumber <
questions.length) {
_controller.nextPage(
duration: const Duration(
milliseconds: 250),
curve: Curves.easeInExpo);
setState(() {
if (option.isCorrect == true) {
_score++;
}
});
setState(() {
_questionNumber++;
// _isLocked = false;
});
} else {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) =>
ResultScreen(
score: _score),
));
}
});
if (_question.isLocked) {
return;
} else {
setState(() {
_question.isLocked = true;
_question.selectedOption = option;
});
}
},
child: Container(
height: 50,
padding: const EdgeInsets.all(12),
margin: const EdgeInsets.symmetric(
vertical: 8),
decoration: BoxDecoration(
color: const Color(0xFF6949FD),
borderRadius:
BorderRadius.circular(16),
border: Border.all(
color: getColorForOption(
option, _question))),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
option.text,
style: const TextStyle(
fontSize: 18,
color: Colors.white),
),
const SizedBox(width: 10),
getIconForOption(option, _question)
],
),
),
))
.toList(),
)))
]);
},
)),
),
],
),
));
}
Color getColorForOption(Option option, Question _question) {
final isSelected = option == _question.selectedOption;
if (_question.isLocked) {
if (isSelected) {
return option.isCorrect ? Colors.green : Colors.red;
} else if (option.isCorrect) {
return Colors.green;
}
}
return const Color(0xFF6949FD);
}
Widget getIconForOption(Option option, Question _question) {
final isSelected = option == _question.selectedOption;
if (_question.isLocked) {
if (isSelected) {
return option.isCorrect
? const Icon(Icons.check_circle, color: Colors.green)
: const Icon(Icons.cancel, color: Colors.red);
} else if (option.isCorrect) {
return const Icon(Icons.check_circle, color: Colors.green);
}
}
return const SizedBox.shrink();
}
}
An easier way is to restart the app when you go back or press a button. You can wrap Scaffold() with WillPopScope() to restart when you back. You can use this package to restart.
If you need to save the score, you can save it in local storage. Another easy package for this is get_storage.
dependencies:
flutter_phoenix: ^1.1.0
runApp(Phoenix(child: const MyApp()));
WillPopScope(
onWillPop: () async {
Phoenix.rebirth(context);
},
child: Scaffold())
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.
I have created a reusable field that is called to display different fields in a form in different screen in my app i have also passed a controller however when dispose the controller it shows this error and if i go back to the same form screen it crashes.
class EntryField extends StatefulWidget {
#override
_EntryFieldState createState() => _EntryFieldState();
final String title;
final TextEditingController controller;
final TextInputType inputType;
final FilteringTextInputFormatter filter;
final hintText;
EntryField({#required this.title,this.hintText,#required this.controller,#required this.inputType,#required this.filter});
}
class _EntryFieldState extends State<EntryField> {
#override
void dispose() {
widget.controller.dispose();
print("anything");
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
this.widget.title,
style: GoogleFonts.quicksand(
fontSize: 18,
)
),
SizedBox(
height: 10,
),
TextFormField(
controller: this.widget.controller,
keyboardType: this.widget.inputType,
inputFormatters: <TextInputFormatter>[
this.widget.filter,
],
validator: (value){
if(value.isEmpty){
return "${this.widget.title} is a Required Field";
}
return null;
},
decoration: InputDecoration(
hintText: this.widget.hintText,
border: InputBorder.none,
fillColor: Color(0xfff3f3f4),
filled: true,
errorBorder: new OutlineInputBorder(
borderSide: new BorderSide(color: Colors.red),
),
errorStyle: TextStyle(
fontSize: 15,
),
),
),
],
),
);
}
}
and in this class i am passing it field values
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final _serviceTitleController = TextEditingController();
final _serviceCategoryController = TextEditingController();
final _servicePriceController = TextEditingController();
ToastErrorMessage _error = ToastErrorMessage();
ToastValidMessage _valid = ToastValidMessage();
class AddServices extends StatefulWidget {
#override
_AddServicesState createState() => _AddServicesState();
}
class _AddServicesState extends State<AddServices> {
int currentIndex;
String _cityName;
final WorkshopServiceQueries _add = WorkshopServiceQueries();
final _firebaseUser = FirebaseAuth.instance.currentUser;
#override
void initState() {
cityName();
super.initState();
currentIndex = 0;
}
#override
void dispose() {
print("hello");
super.dispose();
}
void clearControllerText(){
_serviceTitleController.clear();
_serviceCategoryController.clear();
_servicePriceController.clear();
}
Future cityName() async{
_cityName = await _add.getWorkshopCityName();
}
changePage(int index) {
setState(() {
currentIndex = index;
});
}
validateFields() async{
final ValidateWorkshopServices service = ValidateWorkshopServices();
final int _price = int.tryParse(_servicePriceController.text.trim());
if(!service.validateServiceCategory(_serviceCategoryController.text.trim()) && !service.validateServiceTitle(_serviceTitleController.text.trim()) && !service.validateServicePrice(_price)){
_error.errorToastMessage(errorMessage: "Enter Valid Data in Each Field");
}
else if(!service.validateServiceCategory(_serviceCategoryController.text.trim())){
_error.errorToastMessage(errorMessage: "Service Category Must Only contain Alphabets");
}
else if(!service.validateServiceTitle(_serviceTitleController.text.trim())){
_error.errorToastMessage(errorMessage: "Service Title Must Only contain Alphabets");
}
else if(!service.validateServicePrice(_price)){
_error.errorToastMessage(errorMessage: "Service Price must be less than or equal to 2000");
}
else{
await addService(_price);
}
}
Future<void> addService(int price) async{
try {
Services data = Services(title: _serviceTitleController.text.trim(), category: _serviceCategoryController.text.trim(), price: price, workshopCity: _cityName, workshopId: _firebaseUser.uid);
await _add.addWorkshopService(data);
if(WorkshopServiceQueries.resultMessage == WorkshopServiceQueries.completionMessage){
_valid.validToastMessage(validMessage: WorkshopServiceQueries.resultMessage);
clearControllerText();
Future.delayed(
new Duration(seconds: 2),
(){
Navigator.pop(context);
},
);
}
else{
_error.errorToastMessage(errorMessage: WorkshopServiceQueries.resultMessage);
}
}catch(e){
_error.errorToastMessage(errorMessage: e.toString());
}
}
#override
Widget build(BuildContext context) {
final height = MediaQuery.of(context).size.height;
int _checkboxValue;
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(
'BIKERSWORLD',
style: GoogleFonts.quicksand(
color: Colors.white,
fontSize: 18,
),
),
backgroundColor: Color(0XFF012A4A),
leading: IconButton(icon:Icon(Icons.arrow_back, color: Colors.orange,),
onPressed:() => Navigator.pop(context),
)
),
body: Container(
height: height,
child: Stack(
children: <Widget>[
Container(
padding: EdgeInsets.symmetric(horizontal: 20),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(height: 30,),
_title(),
SizedBox(height: 40),
_addServicesWidget(),
SizedBox(height: 20),
FlatButton(
child: Container(
padding: EdgeInsets.symmetric(vertical: 15),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5)),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.grey.shade200,
offset: Offset(2, 4),
blurRadius: 5,
spreadRadius: 2)
],
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Color(0xfffbb448), Color(0xfff7892b)])),
child: Text(
'Register Now',
style: GoogleFonts.krub(
fontSize: 18,
color: Colors.white,
),
),
),
onPressed: (){
if(!_formKey.currentState.validate()){
return;
}
else{
validateFields();
}
},
),
SizedBox(height: 20),
],
),
),
),
],
),
),
),
);
}
}
Widget _addServicesWidget() {
return Form(
key: _formKey,
autovalidateMode: AutovalidateMode.disabled,
child: Column(
children: <Widget>[
EntryField(title: "Category",hintText: 'Mechanical',controller: _serviceCategoryController,inputType: TextInputType.text,filter: FilteringTextInputFormatter.allow(RegExp("[a-zA-Z ]"))),
SizedBox(height:15,),
EntryField(title: "Title",hintText: 'wheel barring',controller: _serviceTitleController,inputType: TextInputType.text,filter: FilteringTextInputFormatter.allow(RegExp("[a-zA-Z ]"))),
SizedBox(height:15,),
EntryField(title: "Price",hintText: 'price < 2000',controller: _servicePriceController,inputType: TextInputType.number,filter:FilteringTextInputFormatter.digitsOnly),
],
),
);
}
You shouldn't dispose the controller from within your widget, since you are creating it outside the widget and passing a reference to it into the widget.
It looks like your controllers are created in the global scope - if so, and if they are intended to be used throughout the lifetime of the app, you shouldn't dispose them.
So either
don't dispose the controllers if they are globals
or create and dispose them from the same "owner" object
for future comers, in my case i was using dispose twice for the same controller:
//error
void dispose() {
myController.dispose();
myController.dispose();
super.dispose();
}
//ok
void dispose() {
myController.dispose();
super.dispose();
}
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 :)
i want to change the indexvalue (pictogramindex) of one page when we click nextbutton on another screen.I will explain briefly , I have 2 screens in my scenario the first screen contains an image and it's name , a textfield and nextbutton (i have provided a dummy data contains a list of image and it's names) the logic behind this is , when we complete the textfield box and click next button(after validate) the textfield value checks with the correctvalue which i was given in the dummy data and show it's synonym which also provided. when we click the next button we will go to another page which contains the correct answer(passed from first page) and a textfield in this the user can write about the correct answer ( validated) when click next button in this page (till this my applicationworks perfectly) i want to load the first page with it's index updated (+1) which i initialised as 0 (var pictogramindex=0). But in my case when coming back to first page the index is not updating it will automatically stores the initialised value. what i want is i want to update index on the first page when i click next button in the Second page .
my source code of first screen is shown here
class Pictogramscreen extends StatefulWidget {
final int length;
const Pictogramscreen({Key key, this.length}) : super(key: key);
#override
_PictogramscreenState createState() => _PictogramscreenState();
}
class _PictogramscreenState extends State<Pictogramscreen> {
#override
final _Key = GlobalKey<FormState>();
Color defaultcolor = Colors.blue[50];
Color trueColor = Colors.green;
Color falseColor = Colors.red;
Widget defcorrect = Text('');
var pictogramindex = 0;
TextEditingController usertitleInput = TextEditingController();
nextPictogram() {
setState(() {
pictogramindex++;
});
}
fillColor() {
setState(() {
usertitleInput.text == pictdata[pictogramindex]['pictcorrectword']
? defaultcolor = trueColor
: defaultcolor = falseColor;
});
}
correctText() {
setState(() {
usertitleInput.text == pictdata[pictogramindex]['pictcorrectword']
? defcorrect = Text(pictdata[pictogramindex]['pictsynonym'])
: defcorrect = Text(pictdata[pictogramindex]['pictcorrectword']);
});
}
reset() {
setState(() {
defaultcolor = Colors.blue[50];
defcorrect = Text('');
usertitleInput.clear();
});
}
void description(BuildContext ctx) {
Navigator.of(context).pushNamed('/user-description', arguments: {
'id': pictdata[pictogramindex]['pictid'],
'word': pictdata[pictogramindex]['pictcorrectword']
});
}
Widget build(BuildContext context) {
int length = pictdata.length;
return Scaffold(
body: pictogramindex < pictdata.length
? ListView(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 20),
padding: EdgeInsets.all(15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Card(
margin: EdgeInsets.only(top: 20),
child: Image.network(
pictdata[pictogramindex]['pictimg']),
),
SizedBox(
height: 10,
),
Text(
pictdata[pictogramindex]['pictword'],
style: TextStyle(
fontSize: 25,
),
),
SizedBox(
height: 10,
),
//Card(
//color: Colors.blue,
// child: TextField(
// decoration: InputDecoration.collapsed(
// hintText: 'type here'),
//textAlign: TextAlign.center,
// onSubmitted: (value) {
// usertitleInput = value;
// print(usertitleInput);
// },
// ),
//),
Form(
key: _Key,
child: TextFormField(
controller: usertitleInput,
validator: (usertitleInput) {
if (usertitleInput.isEmpty) {
return 'Answer cannot be empty';
} else {
return null;
}
},
textAlign: TextAlign.center,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Colors.blueAccent),
borderRadius: BorderRadius.all(
Radius.circular(15),
)),
labelText: 'Type your Answer',
filled: true,
fillColor: defaultcolor,
),
onFieldSubmitted: (value) {
usertitleInput.text = value;
fillColor();
correctText();
print(usertitleInput.text);
}),
),
SizedBox(
height: 10,
),
defcorrect,
SizedBox(
height: 10,
),
RaisedButton(
onPressed: () {
if (_Key.currentState.validate()) {
description(context);
// nextPictogram();
reset();
}
//
//if (_Key.currentState.validate() == correctText()) {
// nextPictogram;
// }
},
child: Text('Next'),
)
],
),
),
],
)
: Center(
child: Text('completed'),
));
}
}
my source code of the second screen is show here
class Userinputscreen extends StatefulWidget {
final String id;
final String word;
const Userinputscreen({Key key, this.id, this.word}) : super(key: key);
#override
_UserinputscreenState createState() => _UserinputscreenState();
}
class _UserinputscreenState extends State<Userinputscreen> {
final _Keey = GlobalKey<FormState>();
TextEditingController userdescription = TextEditingController();
var pictogramindex;
void nextpict(BuildContext context) {
Navigator.of(context).pushNamed('/main-screen');
}
// void nextpict(BuildContext context, int index) {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (ctx) => Pictogramscreen(
// index: i = 0,
// )));
// }
#override
Widget build(BuildContext context) {
final routeArgs =
ModalRoute.of(context).settings.arguments as Map<String, String>;
final correctWord = routeArgs['word'];
return MaterialApp(
home: Scaffold(
body: ListView(children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 50),
child: Center(
child: Container(
padding: EdgeInsets.all(20),
margin: EdgeInsets.only(top: 100),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
correctWord,
style: TextStyle(fontSize: 26),
),
SizedBox(
height: 10,
),
Form(
key: _Keey,
child: TextFormField(
controller: userdescription,
validator: (userdescription) {
if (userdescription.isEmpty) {
return 'Answer cannot be empty';
} else {
return null;
}
},
textAlign: TextAlign.center,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blueAccent),
borderRadius: BorderRadius.all(
Radius.circular(15),
)),
labelText: 'Type your Answer',
filled: true,
),
onFieldSubmitted: (value) {
userdescription.text = value;
print(userdescription.text);
}),
),
SizedBox(
height: 10,
),
RaisedButton(
onPressed: () {
if (_Keey.currentState.validate()) {
nextpict(context);
}
},
child: Text('Next'),
)
],
),
),
),
),
])),
);
}
}
If I get it right, you basically want to tell the initial page that it's state is updated(the index) elsewhere. You basically need to make your app "reactive".
As is said in Google Developers Tutorial:
One of the advantages of Flutter is that it uses reactive views, which you can take to the next level by also applying reactive principles to your app’s data model.
Use some sort of state management. You need to choose from and use either Bloc, InheritedWidget and InheritedModel, Provider(ScopedModel), or the like.
Check this article on flutter about state management, or this for a complete list of approaches