Flutter ChangeNotifierProvider issue - flutter

I have a ProperyListPage and on each list item clicked this is the action it does.
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChangeNotifierProvider.value(
value: property,
child: PropertyPage(),
),
),
);
}
inside of PropertyPage I have BottomNavigationBar, when you click on Edit BottomNavigationBarItem then it goes to a PropertyEditTab like this
PropertyEditTab(),
inside of PropertyEditTab I have a Form widget like this
child: Form(
key: _formKey,
child: Column(
children: [
PropertyForm(
callback: saveProperty,
),
...
)...
My saveProperty callback looks like this
void saveProperty(BuildContext context) async {
if (_formKey.currentState.validate()) {
PropertyViewModel _propertyVM = Provider.of<PropertyViewModel>(context, listen: false);
savingProperty();
final isSaved = await _propertyVM.updateProperty();
if (isSaved) {
propertySaved();
} else {}
}
}
the updateProperty method does an update to Firestore like this
class PropertyViewModel extends ChangeNotifier {
...
Future<bool> updateProperty() async {
bool isSaved = false;
User user = _firebaseAuth.currentUser;
print(uid);
final property = Property(
user.email,
...
);
try {
await _firestore
.collection(Constants.propertyCollection)
.doc(property.uid)
.update(property.toMap());
isSaved = true;
} on Exception catch (e) {
print(e);
message = 'Property not saved.';
} catch (e) {
print(e);
message = 'Error occurred';
}
notifyListeners();
return isSaved;
}
...
}
then each one of my form fields look like this
FormTextField(
name: 'address_line_1',
hintText: 'Address Line 1',
initialValue: propertyVM.addressLine1 ?? '',
onChanged: (value) {
propertyVM.addressLine1 = value;
},
),
and this is my onPress of button to save property
ThemeButton(
text: 'Save Property',
buttonColor: Constants.kPrimaryColor,
textColor: Colors.white,
onPress: () => widget.callback(context),
),
the issue is that the original PropertyViewModel property isn't updating when notifyProviders() is called and I can't figure out what I am doing wrong.
Thanks in advance for the help.

we were writing on the slack channel.
I think the reason is because in the FormTextField widget change it to this:
FormTextField(
name: 'address_line_1',
hintText: 'Address Line 1',
initialValue: propertyVM.addressLine1 ?? '',
onChanged: (value) {
propertyVM.updateAdressLine(value);
},
),
And in your PropertyViewModel make a function
void updateAdressLine() {
adressLine1 = value;
notifyListeners();
}
It's because from your FormTextField you are only changing the value. You need pack the changes in a method so you can change the value and notifyListeners at one time.

Related

setState not working anymore if page reloads

I'm using the package speech_to_text in order to create a voice recognition page.
On the first load of the page, everything seems to be right and the package works perfectly.
If I go back and reopen the voice recognition page, the setState function doesnt' work anymore.
So I worked to put the code in a custom Dialog() of my CustomDialog() class that id builded from the MainPage() class.
I made this in order to use a single Stateful Widget, otherwise I couldn't update the dialog content.
In this way I get the same issue, since in the app there are direct link to the MainPage().
This will rebuild the MainPage() and when I call my CustomDialog(), the setState() is not working anymore.
Here is the code:
void _listen() async {
if (!_isListening) {
bool available = await _speech.initialize(
finalTimeout: const Duration(seconds: 10),
onStatus: (val) {
print('onStatus: $val');
if (val.contains("done")) {
print('>> STATUS DONE');
setState(() {
_isListening = false;
});
}
},
onError: (val) {
print('onError: $val');
},
);
if (available) {
finale = false;
setState(() {
_isListening = true;
stop = false;
finale = false;
});
_speech.listen(
listenMode: stt.ListenMode.confirmation,
onResult: (val) => setState(() {
_text = val.recognizedWords;
if (val.hasConfidenceRating && val.confidence > 0) {
//_confidence = val.confidence;
}
}),
);
}
} else {
setState(() {
_isListening = false;
});
_speech.stop();
}
}
You can see that there is a print value: "STATUS DONE" and this works.
But the setState() after that, is not working.
Any workaround?
The setState method is used to rebuild UI for Stateful widgets. However, when you open a dialog box, you have to keep in mind that it is itself not a stateful widget. You can use the StatefulBuilder widget to overcome this problem:
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text("Title of Dialog"),
content: Text(contentText),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context),
child: Text("Cancel"),
),
TextButton(
onPressed: () {
setState(() {
contentText = "Changed Content of Dialog";
});
},
child: Text("Change"),
),
],
);
},
);

having trouble with Flutter null safety, database object refuses to initialize

I have been trying to implement a simple To Do app without providers (for the course's sake) to advance in the course at the pace the instructor intended. But I cannot seem to find a solution for this small problem..
in the below code, the variable database does not want to be initialized whatsoever. The getData() method that's supposed to retrieve data (query) doesn't initialize the variable database even if I call it in createDatabase() in initState() It keeps giving the following errors:
''' I/flutter ( 5171): error LateInitializationError: Field 'database' has not been initialized. during open, closing...
E/flutter ( 5171): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: LateInitializationError: Field 'database' has not been initialized.
'''
import 'package:intl/intl.dart';
import 'package:sqflite/sqflite.dart';
import 'package:to_do_app/modules/todo_app/archived/archived_screen.dart';
import 'package:to_do_app/modules/todo_app/done/done_screen.dart';
import 'package:to_do_app/modules/todo_app/tasks/tasks_screen.dart';
class HomeLayout extends StatefulWidget {
const HomeLayout({ Key? key }) : super(key: key);
#override
State<HomeLayout> createState() => _HomeLayoutState();
}
class _HomeLayoutState extends State<HomeLayout> {
int currentIndex = 0;
List<String> titles = [
'Tasks',
'Done Tasks',
'Archived Tasks',
];
List<Map> tasks = [];
late Database database;
var scaffoldKey = GlobalKey<ScaffoldState>();
bool isOpen = false;
IconData fabIcon = Icons.edit;
var titleController = TextEditingController();
var dateController = TextEditingController();
var timeController = TextEditingController();
var formKey = GlobalKey<FormState>();
#override
void initState() {
super.initState();
createDatabase();
}
#override
Widget build(BuildContext context) {
List <Widget> screens = [
NewTasksScreen(),
DoneTasksScreen(),
ArchivedTasksScreen()
];
return Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: Text(titles[currentIndex]),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
if(isOpen == false){
isOpen = true;
setState(() {
fabIcon = Icons.add;
});
scaffoldKey.currentState!.showBottomSheet(
(context) => SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20),
child: Form(
key: formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
validator: (value){
if(value!.isEmpty){
return 'title must not be empty';
}
},
controller: titleController,
decoration: InputDecoration(
labelText: 'Title',
prefixIcon: Icon(
Icons.title
),
),
),
SizedBox(height: 10),
TextFormField(
onTap: (){
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime.now(),
lastDate: DateTime(2025)
).then((value) {
dateController.text = DateFormat.yMMMMd().format(value!);
});
},
validator: (value){
if(value!.isEmpty){
return 'date must not be empty';
}
},
controller: dateController,
decoration: InputDecoration(
labelText: 'Date',
prefixIcon: Icon(
Icons.date_range
),
),
),
SizedBox(height: 10),
TextFormField(
onTap: (){
showTimePicker(
context: context,
initialTime: TimeOfDay.now()
).then((value) {
timeController.text = value!.format(context);
});
},
validator: (value){
if(value!.isEmpty){
return 'time must not be empty';
}
},
controller: timeController,
decoration: InputDecoration(
labelText: 'Time',
prefixIcon: Icon(
Icons.timer
),
),
),
],
),
),
),
),
).closed.then(
(value) {
isOpen = false;
}
);
} else {
if (formKey.currentState!.validate()) {
insertIntoDatabase(
title: titleController.text,
date: dateController.text,
time: timeController.text
).then((value) {
Navigator.pop(context);
isOpen = false;
setState(() {
fabIcon = Icons.edit;
});
});
}
}
},
child: Icon(
fabIcon
),
),
bottomNavigationBar: BottomNavigationBar(
elevation: 20,
type: BottomNavigationBarType.fixed,
currentIndex: currentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(
Icons.task_alt
),
label: 'new tasks'
),
BottomNavigationBarItem(
icon: Icon(
Icons.done
),
label: 'done tasks'
),
BottomNavigationBarItem(
icon: Icon(
Icons.archive_outlined
),
label: 'archived tasks'
)
],
onTap: (index){
setState(() {
currentIndex = index;
});
},
),
body: screens[currentIndex],
);
}
Future getName() async {
return 'Ahmad Ali';
}
void createDatabase() async {
database = await openDatabase(
'todo.db',
version: 1,
onCreate: (database, version) {
print('databse created');
database.execute(
'CREATE TABLE tasks(id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, date TEXT, time TEXT, status TEXT)'
).then(
(value) {
print('table created');
}
).catchError(
(error){
print('error creating table ${error.toString()}');
}
);
},
onOpen: (databse) async {
print('database opened');
await getData(database).then((value) {
tasks = value;
print(tasks);
});
}
);
}
Future insertIntoDatabase({
required String title,
required String date,
required String time
}
) async {
return await database.transaction(
(txn) {
return txn.rawInsert(
'INSERT INTO tasks (title, date, time, status) VALUES ("$title", "$date", "$timeController", "new")'
).then(
(value) {
print('$value inserted succsessfully');
}
).catchError(
(error){
print('error inserting into table ${error.toString()}');
}
);
}
);
}
Future<List<Map>> getData(Database database) async {
return await database.rawQuery('SELECT * FROM tasks');
}
} ```
Try to initialize
late Database database;
in the StatefulWidget instead of it's State and in the createDatabase() call it like
widget.database = await OpenDatabase...
Also try to see if adding await in initState helps.
instead of
late Database database;
Write
static Database? database;

Snackbar showing twice due to stacked screen Flutter, How can i avoid it?

I don't want to show Snackbar on stacked screen. When user taps on Signup from LoginScreen. Then, SignUpScreen stacked over LoginScreen. but the problem is both implements same ProviderListener and therefore, It shows Snackbar multiple times. How can I avoid it? , How can I make sure that, If current route is SignUp then show Snackbar
UserAuthService.dart
import 'package:notifications/domain/repository/firebase_repository/firebase_user_repo.dart';
import 'package:notifications/domain/services/auth_service/all_auth_builder.dart';
import 'package:notifications/export.dart';
import 'package:notifications/resources/local/local_storage.dart';
enum AuthenticationStatus {
loading,
error,
success,
}
class UserAuthService extends ChangeNotifier {
final authBuilder = AllTypeAuthBuilder();
String? _errorMsg, _sessionID;
AuthenticationStatus _status = AuthenticationStatus.loading;
EmailLinkAuthenticationRepo? _repo;
String? get errorMsg => this._errorMsg;
void get _setDefault {
_errorMsg = null;
//_sessionID = null;
}
String? get sessionID {
return LocallyStoredData.getSessionID();
}
void logOut() {
return LocallyStoredData.deleteUserKey();
}
Future<bool> userExists(String userID) async {
final isExists = await authBuilder.checkUserExists(userID);
return isExists ? true : false;
}
Future<bool> login() async {
_setDefault;
try {
await authBuilder.login();
return true;
} on BaseException catch (e) {
log("Exception $e");
_errorMsg = e.msg;
notifyListeners();
return false;
}
}
Future<bool> register(String username, String email, String password) async {
_setDefault;
try {
await authBuilder.register(username, email, password);
return true;
} on BaseException catch (e) {
log("Exception $e");
_errorMsg = e.msg;
notifyListeners();
return false;
}
}
Future<bool> signIn(String userID, String password) async {
_setDefault;
try {
await authBuilder.signIn(userID, password);
return true;
} on BaseException catch (e) {
log("Exception ${e.msg}");
_errorMsg = e.msg;
notifyListeners();
}
return false;
}
void loginWithEmail(String email) async {
_setDefault;
try {
_repo = await authBuilder.loginWithEmail(email);
_repo!.onLinkListener(
onSuccess: _onSuccess,
onError: _onError,
);
} on BaseException catch (e) {
log("Exception ${e.msg}");
_errorMsg = e.msg;
}
notifyListeners();
}
Future<bool> _onSuccess(PendingDynamicLinkData? linkData) async {
_setDefault;
try {
log("OnLinkAuthenticate");
await _repo!.onLinkAuthenticate(linkData);
return true;
} on BaseException catch (e) {
log("Error onSucess: $e");
_errorMsg = e.msg;
notifyListeners();
}
return false;
}
Future<dynamic> _onError(OnLinkErrorException? error) async {
log("Error $error in Link");
}
Future<void> tryTo(Function callback) async {
try {
await callback();
} on BaseException catch (e) {
_errorMsg = e.msg;
}
}
}
LoginScreen.dart
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _userIDController = TextEditingController(),
_passwordController = TextEditingController();
final _formKey = GlobalKey<FormState>();
bool? isAuthenticated;
#override
initState() {
super.initState();
}
#override
void dispose() {
_userIDController.dispose();
_passwordController.dispose();
super.dispose();
}
_onGoogleLogin() async {
context.read(loginPod).login();
}
_onLoginButtonTap() {
networkCheckCallback(context, () async {
if (_formKey.currentState!.validate()) {
WidgetUtils.showLoaderIndicator(context, 'Loading...');
final isSignedIn = await context
.read(loginPod)
.signIn(_userIDController.text, _passwordController.text);
Navigator.pop(context);
if (isSignedIn) Beamer.of(context).beamToNamed(Routes.home);
}
});
}
_resetAuthenticateState() {
if (isAuthenticated != null)
setState(() {
isAuthenticated = null;
});
}
onUsernameChange(String? value) async {
final error = await hasNetworkError();
if (_userIDController.text.isNotEmpty && error == null) {
isAuthenticated = await context.read(loginPod).userExists(value!);
setState(() {});
return;
}
_resetAuthenticateState();
}
onPasswordChange(String? value) {
//Code goes here....
}
loginWith(BuildContext context, LoginType type) {
switch (type) {
case LoginType.emailLink:
Beamer.of(context).beamToNamed(Routes.email_link_auth);
break;
case LoginType.idPassword:
Beamer.of(context).beamToNamed(Routes.login_id_pass);
break;
case LoginType.googleAuth:
Beamer.of(context).beamToNamed(Routes.login_with_google);
break;
case LoginType.unknown:
Beamer.of(context).beamToNamed(Routes.register);
break;
}
}
Future<bool> _onBackPress(_) async {
return await showDialog<bool>(
context: _,
builder: (context) {
return AlertDialog(
title: Text("Do you want to exit?"),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text("OK")),
TextButton(
onPressed: () {
Beamer.of(_).popRoute();
},
child: Text("Cancel"))
],
);
}) ??
false;
}
_onLoginStatus(BuildContext _, UserAuthService service) {
if (service.errorMsg != null)
_.showErrorBar(
content: Text(
"WithLogin" + service.errorMsg!,
style: TextStyle(fontSize: 12.sp),
));
}
#override
Widget build(BuildContext _) {
return ProviderListener(
onChange: _onLoginStatus,
provider: loginPod,
child: WillPopScope(
onWillPop: () => _onBackPress(_),
child: Scaffold(
body: SingleChildScrollView(
child: SizedBox(height: 1.sh, child: _buildLoginScreen())),
),
),
);
}
Widget _buildLoginScreen() {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
//_buildVrtSpacer(60),
_buildHeading(),
//_buildVrtSpacer(30),
_buildForm(),
//_buildVrtSpacer(30),
_buildIconButtons(),
_buildSignUpButton(),
],
);
}
BoldHeadingWidget _buildHeading() =>
BoldHeadingWidget(heading: AppStrings.login);
ResponsiveVrtSpacer _buildVrtSpacer(double value) =>
ResponsiveVrtSpacer(space: value);
Widget _buildForm() {
return CustomForm(
formKey: _formKey,
child: Column(
children: [
_buildUsernameField(),
_buildVrtSpacer(10),
_buildPasswordField(),
_buildForgetPassword(),
_buildLoginButton(),
],
),
);
}
Widget _buildPasswordField() {
return CustomTextFieldWithLabeled(
controller: _passwordController,
label: AppStrings.password,
hintText: AppStrings.password,
onValidate: (String? value) =>
(value!.isEmpty) ? AppStrings.emptyPasswordMsg : null,
obscureText: true,
onChange: onPasswordChange,
icon: CupertinoIcons.lock);
}
Widget _buildUsernameField() {
return CustomTextFieldWithLabeled(
controller: _userIDController,
label: AppStrings.usernameOrEmail,
hintText: AppStrings.usernameOrEmail1,
icon: CupertinoIcons.person,
onChange: onUsernameChange,
onValidate: (String? value) =>
(value!.isEmpty) ? AppStrings.emptyUserIDMsg : null,
suffixIcon: isAuthenticated == null
? null
: (isAuthenticated!
? CupertinoIcons.checkmark_alt_circle_fill
: CupertinoIcons.clear_circled_solid),
suffixColor: isAuthenticated == null
? null
: (isAuthenticated! ? Colors.green : Styles.defaultColor));
}
Widget _buildIconButtons() {
return Column(
children: [
Text("Or", style: TextStyle(fontSize: 14.sp)),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(width: 10),
_buildIconButton(
iconPath: 'assets/icons/email-icon.svg', onTap: () {}),
const SizedBox(width: 8),
_buildIconButton(
iconPath: 'assets/icons/icons8-google.svg',
onTap: _onGoogleLogin),
],
),
],
);
}
Widget _buildIconButton(
{required String iconPath, required VoidCallback onTap}) {
return GestureDetector(
onTap: onTap,
child: SvgPicture.asset(
iconPath,
height: 30.sp,
width: 30.sp,
));
}
Widget _buildSignUpButton() {
return Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
AppStrings.dontHaveAccount,
style: TextStyle(color: Colors.black54, fontSize: 14.sp),
),
const SizedBox(height: 5),
CustomTextButton(
title: "Sign Up",
onPressed: () => Beamer.of(context).beamToNamed(Routes.register)),
],
);
}
Widget _buildLoginButton() {
return DefaultElevatedButton(
onPressed: _onLoginButtonTap,
title: AppStrings.login,
);
}
Widget _buildForgetPassword() {
return Align(
alignment: Alignment.centerRight,
child: TextButton(
style: ButtonStyle(
overlayColor: MaterialStateProperty.all(Color(0x11000000)),
foregroundColor: MaterialStateProperty.all(Color(0x55000000)),
),
onPressed: () {},
child: Text("Forget Password?"),
),
);
}
}
SignUpScreen.dart
class SignUp extends StatefulWidget {
const SignUp({Key? key}) : super(key: key);
#override
_SignUpState createState() => _SignUpState();
}
class _SignUpState extends State<SignUp> {
final _formKey = GlobalKey<FormState>();
final _usernameController = TextEditingController(text: "hammad11"),
_emailController = TextEditingController(text: "mason#gmail.com"),
_passwordController = TextEditingController(text: "ha11"),
_confirmPassController = TextEditingController(text: "ha11");
_onValidate(String? value, ValidationType type) {
switch (type) {
case ValidationType.username:
if (value!.isNotEmpty && value.length < 8)
return "Username Must Be 8 characters long";
else if (value.isEmpty) return "Username required";
return null;
case ValidationType.email:
if (value!.isEmpty)
return "Email required";
else if (!value.isEmail) return "Please enter a Valid Email";
return null;
case ValidationType.password:
if (value!.isEmpty)
return "Password required";
else if (value.isAlphabetOnly || value.isNumericOnly)
return "Password must be AlphaNumeric";
return null;
case ValidationType.confirmPassword:
if (value!.isEmpty)
return "Confirm Password required";
else if (value != _passwordController.text)
return "Password doesn't match";
return null;
}
}
_onRegister() async {
//Clears any snackbar opened due to Error or Multiple clicks
//ScaffoldMessenger.of(context).clearSnackBars();
log("SignUp -> _onRegisterTap ");
if (_formKey.currentState!.validate()) {
WidgetUtils.showLoaderIndicator(context, "Please wait! Loading.....");
final isLoggedIn = await context.read(loginPod).register(
_usernameController.text,
_emailController.text,
_passwordController.text,
);
await Beamer.of(context).popRoute();
if (isLoggedIn) Beamer.of(context).beamToNamed(Routes.login);
} else
log("Form Input Invalid");
}
_onChanged(_, UserAuthService service) async {
if (service.errorMsg != null) WidgetUtils.snackBar(_, service.errorMsg!);
// if (!service.isLoading) await Beamer.of(context).popRoute();
// if (service.taskCompleted) {
// log("User Added Successfully");
// Beamer.of(context).popToNamed(
// Routes.login_id_pass,
// replaceCurrent: true,
// );
// } else {
// WidgetUtils.snackBar(context, service.errorMsg!);
// }
}
_alreadyHaveAccount() => Beamer.of(context).popToNamed(Routes.main);
#override
Widget build(BuildContext context) {
return ProviderListener(
provider: loginPod,
onChange: _onChanged,
child: Scaffold(
body: LayoutBuilder(builder: (context, constraints) {
return SingleChildScrollView(
child: SizedBox(
height: 1.sh,
child: Column(
children: [
_buildSpacer(50),
BoldHeadingWidget(heading: "Sign Up"),
CustomForm(
child: Column(
children: [
CustomTextFieldWithLabeled(
label: "Username",
hintText: "Type Username",
onValidate: (value) =>
_onValidate(value, ValidationType.username),
controller: _usernameController,
icon: CupertinoIcons.person),
CustomTextFieldWithLabeled(
label: "Email",
hintText: "Type Email",
controller: _emailController,
onValidate: (value) =>
_onValidate(value, ValidationType.email),
icon: CupertinoIcons.envelope),
CustomTextFieldWithLabeled(
label: "Password",
hintText: "Type Password",
controller: _passwordController,
onValidate: (value) =>
_onValidate(value, ValidationType.password),
icon: CupertinoIcons.lock),
CustomTextFieldWithLabeled(
label: "Confirm Password",
hintText: "Type Confirm Password",
onValidate: (value) => _onValidate(
value, ValidationType.confirmPassword),
controller: _confirmPassController,
icon: CupertinoIcons.lock),
Row(
children: [
Checkbox(
value: true,
onChanged: (value) {},
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
),
Flexible(
child: Text(
"I accept Terms & Conditions and the Privacy Policy",
style: TextStyle(fontSize: 13.sp),
),
),
],
),
_buildSpacer(10),
DefaultElevatedButton(
title: "Sign Up",
onPressed: _onRegister,
),
],
),
formKey: _formKey,
),
const SizedBox(height: 10),
CustomTextButton(
onPressed: () => Beamer.of(context)
.popToNamed(Routes.login, stacked: false),
title: AppStrings.alreadyHaveAccount,
),
// Spacer(flex: 2),
],
),
),
);
}),
),
);
}
ResponsiveVrtSpacer _buildSpacer(double value) =>
ResponsiveVrtSpacer(space: value.h);
}
showsnackbar(String message, context) {
final snackbar = SnackBar(
content: Text(message),
duration: Duration(seconds: 2),
);
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(snackbar);
}
Use this function to call snackbar it will remove the current snackbar and will only show the latest.

Showing Error Text on TextField in ShowDialog

I am trying to build an AlertDialog to reauthenticate user before deleting account. I would like to show error text when reauthentication does not work.
The errors are handled correctly as I can see by using print() statements. However I cannot get the TextField to show the error as a feedback to the user. I think it must be something to do with the state of the SimpleDialog itself, rather than the state of the outer widget...
Here is the code:
Future _validate({String email, String password}) async {
setState(() {
_wrongEmail = false;
_wrongPassword = false;
_emailErrorText = null;
_passwordErrorText = null;
});
try {
if (await userDataBase.checkCorrectEmail(
email: email, loggedInUser: widget.user.id)) {
throw FirebaseAuthException(
message: 'email is not correct', code: 'user-not-found');
}
await userDataBase.authenticateUser(email, password);
} catch (e) {
if (e.code == 'wrong-passwod') {
setState(() {
_passwordErrorText = 'Password not Correct';
});
} else if (e.code == 'user-not-found') {
setState(() {
_emailErrorText = 'Email not Correct';
});
} else {
setState(() {
_emailErrorText = 'Email not Correct';
_passwordErrorText = 'Password not Correct';
_wrongPassword = true;
_wrongEmail = true;
});
}
throw e;
}
}
Future _authenticate({String email, String password}) async {
try {
await _validate(email: email, password: password);
} catch (e) {
print(e.code);
print(_wrongEmail);
print(_emailErrorText);
return;
}
print('correct behaviour');
// Navigator.pop(context);
// setState(() {
// _loading = true;
// });
// await userDataBase.deleteUser(widget.user.id);
// Navigator.of(context).popUntil((route) => route.isFirst);
}
Future _confirmIdentity() {
final emailController = TextEditingController();
final passwordController = TextEditingController();
_emailErrorText = null;
_passwordErrorText = null;
return showDialog(
context: context,
builder: (context) => AlertDialog(
content: Container(
height: 200,
child: Column(
children: [
Text('Confirm Your Identity'),
TextField(
controller: emailController,
decoration: InputDecoration(
errorText: _wrongEmail ? _emailErrorText : null,
labelText: 'email'),
),
TextField(
controller: passwordController,
decoration:
InputDecoration(
errorText: _wrongPassword ? _passwordErrorText : null,
labelText: 'password'
),
),
Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FlatButton(
onPressed: () {
_authenticate(
email: emailController.text,
password: passwordController.text);
},
child: Text('Confirm'),
),
FlatButton(
onPressed: () => Navigator.pop(context),
child: Text('Cancel'),
),
],
)
],
),
),
),
);
}
Thank you. I can post all the widget code ecc. if you need it: it is a statefull widget.
For 1st 2 if conditions, you are setting the error message, but not setting the respective boolean flags to true.
Example:
if (e.code == 'wrong-passwod') {
setState(() {
_passwordErrorText = 'Password not Correct';
});
}
Here, in the setState()function, you will also need to set _wrongPassword = true;
I fixed the issue. The boolean variables _wrongPassword, _wrongEmail are not necessary seen as when the _PasswordErrorText or _EmailErrorText are null, the error text is not displayed by default.
It is necessary to build a statefull widget that is the content of ShowDialog! then everything works.

the argument type 'JsObject cant be assigned to the parameter type 'BuildContext '

hi guys so i was trying to add some error check code in my page 'product_edit' to handle any potential http response error
so this is my page
product_edit.dart
==============================
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import '../models/product.dart';
import '../scoped-models/main.dart';
class ProductEdit extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _ProductEditState();
}
}
class _ProductEditState extends State<ProductEdit> {
final Map<String, dynamic> _formData = {
'title': null,
'description': null,
'price': null,
'image':
'https://cdn.pixabay.com/photo/2020/03/04/07/28/black-4900736_960_720.jpg'
};
//String _title;
// String _description;
// double _price;
final GlobalKey<FormState> _formkey = GlobalKey<FormState>();
Widget _buildtypetextfield(Product product) {
return TextFormField(
decoration: InputDecoration(labelText: 'Product Type'),
initialValue: product == null ? '' : product.title,
validator: (String value) {
if (value.isEmpty) {
return 'product type and name is required';
}
return null;
},
onSaved: (String value) {
_formData['title'] = value;
},
);
}
Widget _builddescriptiontextfield(Product product) {
return TextFormField(
maxLines: 6,
decoration: InputDecoration(labelText: 'Description'),
initialValue: product == null ? '' : product.description,
validator: (String value) {
if (value.isEmpty || value.length < 10) {
return 'product description is required,and should be 10+ characters long';
}
return null;
},
onSaved: (String value) {
_formData['description'] = value;
},
);
}
Widget _buildpricetextfield(Product product) {
return TextFormField(
keyboardType: TextInputType.number,
decoration: InputDecoration(labelText: 'Price'),
initialValue: product == null ? '' : product.price.toString(),
validator: (String value) {
if (value.isEmpty ||
!RegExp(r'^(?:[1-9]\d*|0)?(?:\.\d+)?$').hasMatch(value)) {
return 'product price is required,and should be a number';
}
return null;
},
onSaved: (String value) {
_formData['price'] = double.parse(value);
},
);
}
Widget _buildSubmitButton() {
return ScopedModelDescendant<MainModel>(
builder: (BuildContext context, Widget child, MainModel model) {
return model.isloading
? Center(child: CircularProgressIndicator())
: RaisedButton(
child: Text('Save'),
textColor: Colors.white,
onPressed: () => _submitform(
model.addproduct,
model.updateproduct,
model.selectproduct,
model.selectedProductIndex),
);
},
);
}
Widget _buildPageContent(BuildContext context, Product product) {
final double deviceWidth = MediaQuery.of(context).size.width;
final double targetWidth = deviceWidth > 550.0 ? 500.0 : deviceWidth * 0.95;
final double targetPadding = deviceWidth - targetWidth;
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Container(
margin: EdgeInsets.all(10.0),
child: Form(
key: _formkey,
child: ListView(
padding: EdgeInsets.symmetric(horizontal: targetPadding / 2),
children: <Widget>[
_buildtypetextfield(product),
_builddescriptiontextfield(product),
_buildpricetextfield(product),
SizedBox(
height: 10.0,
),
_buildSubmitButton(),
],
),
),
),
);
}
void _submitform(
Function addproduct, Function updateproduct, Function setSelectedProduct,
[int selectedproductIndex]) {
if (!_formkey.currentState.validate()) {
return;
}
_formkey.currentState.save();
//final Map<String, dynamic> product = {
// 'title': _title,
// 'description': _description,
// 'price': _price,
// 'image':
// 'https://cdn.pixabay.com/photo/2020/03/04/07/28/black-4900736_960_720.jpg'
// };
if (selectedproductIndex == -1) {
addproduct(_formData['title'], _formData['description'],
_formData['image'], _formData['price'])
.then((_) => Navigator.pushReplacementNamed(context, '/products')
.then((_) => setSelectedProduct(null)));
} else {
updateproduct(_formData['title'], _formData['description'],
_formData['image'], _formData['price'])
.then((_) => Navigator.pushReplacementNamed(context, '/products')
.then((_) => setSelectedProduct(null)));
}
}
#override
Widget build(BuildContext context) {
return ScopedModelDescendant<MainModel>(
builder: (BuildContext context, Widget child, MainModel model) {
final Widget pageContent =
_buildPageContent(context, model.selectedproduct);
return model.selectedProductIndex == -1
? pageContent
: Scaffold(
appBar: AppBar(
title: Text('Edit Product'),
),
body: pageContent,
);
},
);
}
}
================
and the error occurred in the '_submitform' function block as i was typing the argument context in the show dialog widget
it's a capture of the error
and here is the function , thank u all very much, thanks in advance
_submitform
void _submitform(
Function addproduct, Function updateproduct, Function setSelectedProduct,
[int selectedproductIndex]) {
if (!_formkey.currentState.validate()) {
return;
}
_formkey.currentState.save();
//final Map<String, dynamic> product = {
// 'title': _title,
// 'description': _description,
// 'price': _price,
// 'image':
// 'https://cdn.pixabay.com/photo/2020/03/04/07/28/black-4900736_960_720.jpg'
// };
if (selectedproductIndex == -1) {
addproduct(_formData['title'], _formData['description'],
_formData['image'], _formData['price'])
.then((bool success) {
if (success) {
Navigator.pushReplacementNamed(context, '/products')
.then((_) => setSelectedProduct(null));
} else {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('something went wrong'),
content: Text('please try again!'),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('Okay'),
)
],
);
});
}
});
} else {
updateproduct(_formData['title'], _formData['description'],
_formData['image'], _formData['price'])
.then((_) => Navigator.pushReplacementNamed(context, '/products')
.then((_) => setSelectedProduct(null)));
}
}
showDialog and Navigator requires BuildContext. In your _submitform function add a parameter : BuildContext.
void _submitform(BuildContext context,
Function addproduct, Function updateproduct, Function setSelectedProduct,
[int selectedproductIndex]) {
// code
}
Pass BuildContext here in _submitform function and use that context in showDialog and Navigator. Again you are using _submitform function inside _buildSubmitButton function. So again add a BuildContext parameter in _buildSubmitButton function and pass it to _submitform function and so on. Ultimately you have to pass BuildContext of build function to the _submitform function. This is the flow as per your current code.