Stateful widget not rebuilding child widget when state changes (Flutter) - flutter

I'm building a form that signs up the user on Firebase, and where the ElevatedButton either displays "CREATE YOUR ACCOUNT" or a CircularProgressIndicator, based on the state of isLoading. Yet, the button's child doesn't change based on isLoading.
The states does change -- I'm printing out isLoading in the middle of my onPressed methode, and I do see it as true.
Why isn't the button changing its child?
import 'package:flutter/material.dart';
import 'package:tedy/components/custom_text_form_field.dart';
import 'package:tedy/components/custom_wrapper.dart';
class EmployerOnboardingOneView extends StatefulWidget {
const EmployerOnboardingOneView({Key? key}) : super(key: key);
#override
State<EmployerOnboardingOneView> createState() =>
_EmployerOnboardingOneViewState();
}
class _EmployerOnboardingOneViewState extends State<EmployerOnboardingOneView> {
var firstNameController = TextEditingController();
var lastNameController = TextEditingController();
var emailController = TextEditingController();
var passwordController = TextEditingController();
var passwordConfirmController = TextEditingController();
bool isLoading = false;
#override
Widget build(BuildContext context) {
var screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
centerTitle: true,
title: SizedBox(
height: 50,
child: Image.asset(
'assets/images/tedy_logo_color.png',
filterQuality: FilterQuality.high,
),
),
),
body: CustomWrapper(
maxWidth: 400,
children: [
Text(
'Create a Tedy account for your company',
style: Theme.of(context).textTheme.headline3,
textAlign: TextAlign.center,
),
const SizedBox(
height: 20,
),
CustomTextFormField(
controller: firstNameController, label: 'First Name'),
const SizedBox(
height: 20,
),
CustomTextFormField(
controller: lastNameController, label: 'Last Name'),
const SizedBox(
height: 20,
),
CustomTextFormField(controller: emailController, label: 'Email'),
const SizedBox(
height: 20,
),
CustomTextFormField(
controller: passwordController,
isPassword: true,
label: 'Password'),
const SizedBox(
height: 20,
),
CustomTextFormField(
controller: passwordConfirmController,
isPassword: true,
label: 'Confirm your password'),
const SizedBox(
height: 20,
),
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Theme.of(context).primaryColor),
onPressed: () async {
setState(() {
isLoading = true;
});
if (passwordController.text != passwordConfirmController.text) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Passwords must match'),
backgroundColor: Colors.red,
),
);
} else {
try {
FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: emailController.text,
password: passwordController.text)
.then((value) {
Navigator.pushNamedAndRemoveUntil(
context, '/', (route) => false);
});
print(isLoading);
} on FirebaseAuthException catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(e.message != null
? e.message as String
: 'There was an error.'),
backgroundColor: Colors.red,
),
);
}
}
setState(() {
isLoading = false;
});
},
child: isLoading
? const SizedBox(
width: 30,
height: 30,
child: CircularProgressIndicator(
color: Colors.white,
),
)
: const Text('CREATE YOUR ACCOUNT'),
),
)
],
),
);
}
}

You are using then for the future. so the last isLoading = false; executed when you pressed the button. You can change state inside .then instead of end. Also you can add inside FirebaseAuthException. make sure to remove last isLoading = false;
FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: emailController.text,
password: passwordController.text)
.then((value) {
setState(() {
isLoading = false;
});
Navigator.pushNamedAndRemoveUntil(
context, '/', (route) => false);
});

createUserWithEmailAndPassword is async, so you have to await it
await FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: emailController.text,
password: passwordController.text);
Navigator.pushNamedAndRemoveUntil(
context, '/', (route) => false);
Your code set isLoading to true, does the password logic and calls this method. But since you don't await it here it just skips it and calls the setState with isLoading = false.

Related

Error: NoSuchMethodError: 'then' Dynamic call of null. Receiver: null

Does anyone know the cause of this error? I have tried many ways but still don't know where the problem is.
Database.dart
import 'package:fitness_app/Login/login_data.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DatabaseService {
static final DatabaseService _databaseService = DatabaseService._internal();
factory DatabaseService() => _databaseService;
DatabaseService._internal();
static Database? _database;
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
Future<Database> _initDatabase() async {
final databasePath = await getDatabasesPath();
final path = join(databasePath, 'conference.database');
return await openDatabase(
path,
onCreate: _onCreate,
version: 1,
onConfigure: (db) async => await db.execute('PRAGMA foreign_keys = ON'),
);
}
Future<void> _onCreate(Database db, int version) async {
await db.execute(
'CREATE TABLE login(id INTEGER PRIMARY KEY, name TEXT, username TEXT, password TEXT)',
);
}
verifyuser(String user, String pass) {}
insertLoginData(Logindata logindata) {}
}
Login.dart
import 'package:fitness_app/Login/signup.dart';
import 'package:flutter/material.dart';
import 'login_data.dart';
import 'package:fitness_app/Database/database.dart';
import 'package:fitness_app/home_page.dart';
class Login extends StatefulWidget {
const Login({Key? key, this.login}) : super(key: key);
final Logindata? login;
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
String email = "a";
String pass = "a";
TextEditingController emails = TextEditingController();
TextEditingController password = TextEditingController();
final _formKey = GlobalKey<FormState>();
static final List<Logindata> _login = [];
final DatabaseService _databaseService = DatabaseService();
Future<List<Logindata>> _getLogin() async {
await _databaseService.verifyuser(email, pass).then((value) {
if (value) {
AlertDialog alert = AlertDialog(
title: const Text('Login successful!'),
content: const Text('Welcome!'),
actions: <Widget>[
TextButton(
onPressed: () => (Navigator.push(
context,
MaterialPageRoute(builder: (context) => const HomePage()),
)),
child: const Text('OK'),
),
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
} else {
AlertDialog alert = AlertDialog(
title: const Text('Error!'),
content: const Text('Wrong Email or Password'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
});
return _login;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SingleChildScrollView(
child: Column(
children: [
Container(
padding: EdgeInsets.all(10),
child: Form(
key: _formKey,
child: Column(
children: [
Text(
'BeFit:Fitness Activity Tracker Progress\n\n',
style: TextStyle(fontSize: 24),
textAlign: TextAlign.start,
),
Text(
'Welcome',
style: TextStyle(fontSize: 40),
textAlign: TextAlign.center,
),
SizedBox(
height: 30,
),
Container(
child: TextFormField(
controller: emails,
decoration: InputDecoration(
border: UnderlineInputBorder(
borderSide:
BorderSide(width: 1, color: Colors.grey)),
labelText: 'Email'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
return null;
},
),
),
SizedBox(
height: 20,
),
Container(
child: TextFormField(
controller: password,
obscureText: true,
decoration: InputDecoration(
border: UnderlineInputBorder(
borderSide:
BorderSide(width: 1, color: Colors.grey)),
labelText: 'Password'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}
return null;
},
),
),
SizedBox(
height: 20,
),
Container(
width: MediaQuery.of(context).size.width,
height: 50,
child: FlatButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
email = emails.text;
pass = password.text;
_getLogin();
print(email);
print(pass);
print('success');
}
},
child: Text("Login"),
textColor: Colors.white,
color: Colors.deepPurple[400],
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(10)),
),
),
SizedBox(
height: 20,
),
Container(
child: Row(
children: <Widget>[
Text('Does not have account?'),
FlatButton(
textColor: Colors.deepPurpleAccent[100],
child: Text(
'Sign up',
style: TextStyle(fontSize: 16),
),
onPressed: () {
//signup screen
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SignUp()),
);
},
)
],
mainAxisAlignment: MainAxisAlignment.center,
))
],
),
),
)
],
),
),
),
);
}
}
Here is the error
enter image description here
I'm using this flutter to complete my project but there are some errors that I can't solve it. Sorry if my coding looks not right because still lacks in coding
Your verifyuser function does not return anything so your value returns null. You either need to return something or check for null values in your then statement.
Future<String> verifyuser(String user, String pass)async {
return user + pass;
}

How to show loading spinner in the button when sign in using firebase

i have create a variable "isLoading" for calling the loader.
the actual problem is when i hit simply in the "signIn" button without give any value in the test field, it will start loading for delay time
how can i stop this and also catch the error when hit the "signIn" button without given any value....
this is my Repo: food delivery app
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import '../../config/constants.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({Key? key}) : super(key: key);
#override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final emailController = TextEditingController();
final passwordController = TextEditingController();
#override
void dispose() {
emailController.dispose();
passwordController.dispose();
super.dispose();
}
bool isLoading = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text('login'),
htspace20,
TextField(
controller: emailController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
label: Text('Email'),
),
),
htspace20,
TextField(
controller: passwordController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
label: Text('Password'),
),
),
htspace40,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(onPressed: () {}, child: const Text('Sign up'))
],
),
SizedBox(
width: double.maxFinite,
height: 40,
child: ElevatedButton(
style: ButtonStyle(
elevation: MaterialStateProperty.all(0),
),
child: isLoading
? const SizedBox(
height: 30,
width: 30,
child: CircularProgressIndicator(
strokeWidth: 3, color: Colors.white),
)
: const Text('Sign in'),
onPressed: () {
setState(() {
isLoading = true;
});
signIn();
Future.delayed(const Duration(seconds: 3), () {
setState(() {
if (emailController.text != " " ||
passwordController.text != " ") {
isLoading = false;
} else {
isLoading = true;
}
});
});
}),
)
],
),
),
);
}
Future signIn() async {
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: emailController.text.trim(),
password: passwordController.text.trim(),
);
} catch (e) {
print('Email or Password Incorrect');
}
}
}
Why not try it all in your singIn() function like this:
Future signIn() async {
try {
isLoading = true;
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: emailController.text.trim(),
password: passwordController.text.trim(),
);
isLoading = false;
} catch (e) {
isLoading = false;
print('Email or Password Incorrect');
}
}
This way it will stop loading if email or pass is incorrect or missing etc.
I'd also disable the login button if email and pass isn't filled out. Stop them from passing blank values.
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20), // <-- Radius
),
),
child: isLoading
? const SizedBox(
height: 30,
width: 30,
child: CircularProgressIndicator(
strokeWidth: 3, color: Colors.white),
)
: const Text('Sign in'),
onPressed: () {
setState(() {
isLoading = true;
});
signIn();
Future.delayed(const Duration(seconds: 3), () {
setState(() {
if (emailController.text != " " ||
passwordController.text != " ") {
isLoading = false;
} else {
isLoading = true;
}
});
});
}),
it also showing circular loading on the button

flutter app , Unhandled Exception: Null check operator used on a null value

This is a simple flutter app. I am getting this error on my signup page which connects to firebase.
when i try to sign up, it remains on thinking with ⭕ spinning, i am getting this error in the debug console:
VERBOSE-2:ui_dart_state.cc(198)] Unhandled Exception: Null check operator used on a null value
#0 _SignupScreenState.signUpUser package:instagram_flutter/screens/signup_screen.dart:5
any thoughts?
here is the code that I believe is the source of the problem at:
(_image != null
? CircleAvatar
This is that block of code:
Stack(
children: [
_image != null
? CircleAvatar(
radius: 32,
backgroundImage: MemoryImage(_image!),
backgroundColor: Colors.red,
)
: const CircleAvatar(
radius: 32,
backgroundImage: NetworkImage(
'https://i.stack.imgur.com/l60Hf.png'),
backgroundColor: Colors.red,
),
I am new to coding and flutter, what should I put to replace the "?"
here is the entire code for the sign_up_screen (it is taken from a tutorial):
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:image_picker/image_picker.dart';
import 'package:instagram_flutter/resources/auth_methods.dart';
import 'package:instagram_flutter/responsive/mobile_screen_layout.dart';
import 'package:instagram_flutter/responsive/web_screen_layout.dart';
import 'package:instagram_flutter/screens/login_screen.dart';
import 'package:instagram_flutter/utils/colors.dart';
import 'package:instagram_flutter/utils/utils.dart';
import '../responsive/responsive_layout_screen.dart';
import '../widgets/text_field_input.dart';
class SignupScreen extends StatefulWidget {
const SignupScreen({Key? key}) : super(key: key);
#override
_SignupScreenState createState() => _SignupScreenState();
}
class _SignupScreenState extends State<SignupScreen> {
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final TextEditingController _bioController = TextEditingController();
bool _isLoading = false;
Uint8List? _image;
#override
void dispose() {
super.dispose();
_emailController.dispose();
_passwordController.dispose();
_usernameController.dispose();
}
void signUpUser() async {
// set loading to true
setState(() {
_isLoading = true;
});
// signup user using our authmethodds
String res = await AuthMethods().signUpUser(
email: _emailController.text,
password: _passwordController.text,
username: _usernameController.text,
bio: _bioController.text,
file: _image!);
// if string returned is sucess, user has been created
if (res == "success") {
setState(() {
_isLoading = false;
});
// navigate to the home screen
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => const ResponsiveLayout(
mobileScreenLayout: MobileScreenLayout(),
webScreenLayout: WebScreenLayout(),
),
),
);
} else {
setState(() {
_isLoading = false;
});
// show the error
showSnackBar(context, res);
}
}
selectImage() async {
Uint8List im = await pickImage(ImageSource.gallery);
// set state because we need to display the image we selected on the circle avatar
setState(() {
_image = im;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: SafeArea(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 32),
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Flexible(
child: Container(),
flex: 2,
),
SvgPicture.asset(
'assets/ic_instagram.svg',
color: primaryColor,
height: 64,
),
const SizedBox(
height: 64,
),
Stack(
children: [
_image != null
? CircleAvatar(
radius: 64,
backgroundImage: MemoryImage(_image!),
backgroundColor: Colors.red,
)
: const CircleAvatar(
radius: 64,
backgroundImage: NetworkImage(
'https://i.stack.imgur.com/l60Hf.png'),
backgroundColor: Colors.red,
),
Positioned(
bottom: -10,
left: 80,
child: IconButton(
onPressed: selectImage,
icon: const Icon(Icons.add_a_photo),
),
)
],
),
const SizedBox(
height: 24,
),
TextFieldInput(
hintText: 'Enter your username',
textInputType: TextInputType.text,
textEditingController: _usernameController,
),
const SizedBox(
height: 24,
),
TextFieldInput(
hintText: 'Enter your email',
textInputType: TextInputType.emailAddress,
textEditingController: _emailController,
),
const SizedBox(
height: 24,
),
TextFieldInput(
hintText: 'Enter your password',
textInputType: TextInputType.text,
textEditingController: _passwordController,
isPass: true,
),
const SizedBox(
height: 24,
),
TextFieldInput(
hintText: 'Enter your bio',
textInputType: TextInputType.text,
textEditingController: _bioController,
),
const SizedBox(
height: 24,
),
InkWell(
child: Container(
child: !_isLoading
? const Text(
'Sign up',
)
: const CircularProgressIndicator(
color: primaryColor,
),
width: double.infinity,
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(vertical: 12),
decoration: const ShapeDecoration(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
),
color: blueColor,
),
),
onTap: signUpUser,
),
const SizedBox(
height: 12,
),
Flexible(
child: Container(),
flex: 2,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
child: const Text(
'Already have an account?',
),
padding: const EdgeInsets.symmetric(vertical: 8),
),
GestureDetector(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const LoginScreen(),
),
),
child: Container(
child: const Text(
' Login.',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
padding: const EdgeInsets.symmetric(vertical: 8),
),
),
],
),
],
),
),
),
);
}
}
there is also the auth page, which includes a warning regarding
file != null) {:
the warning says:
The operand can't be null, so the condition is always true.
Remove the condition.dartunnecessary_null_comparison
auth_methods.dart:
import 'dart:typed_data';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:instagram_flutter/resources/storage_methods.dart';
class AuthMethods {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final FirebaseAuth _auth = FirebaseAuth.instance;
//sign up user
Future<String> signUpUser({
required String email,
required String password,
required String username,
required String bio,
required Uint8List file,
}) async {
String res = "Some error Occurred";
try {
if (email.isNotEmpty ||
password.isNotEmpty ||
username.isNotEmpty ||
bio.isNotEmpty ||
file != null) {
// registering user in auth with email and password
UserCredential cred = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
String photoUrl = await StorageMethods()
.uploadImageToStorage('profilePics', file, false);
await _firestore.collection('users').doc(cred.user!.uid).set({
'username': username,
'uid': cred.user!.uid,
'email': email,
'bio': bio,
'followers': [],
'following': [],
'photoUrl': photoUrl,
});
res = "success";
} else {
res = "Please enter all the fields";
}
} catch (err) {
return err.toString();
}
return res;
}
// logging in user
Future<String> loginUser({
required String email,
required String password,
}) async {
String res = "Some error Occurred";
try {
if (email.isNotEmpty || password.isNotEmpty) {
// logging in user with email and password
await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
res = "success";
} else {
res = "Please enter all the fields";
}
} catch (err) {
return err.toString();
}
return res;
}
Future<void> signOut() async {
await _auth.signOut();
}
}
Make sure that the declaration for _image is of type MemoryImage like this: MemoryImage? _image; Then, when using the value for the background image, just use _image, not MemoryImage(_image!).
Try using this way..
Stack(
children: [
_image == null
? const CircleAvatar(
radius: 32,
backgroundImage: NetworkImage(
'https://i.stack.imgur.com/l60Hf.png'),
backgroundColor: Colors.red,
):
CircleAvatar(
radius: 32,
backgroundImage: MemoryImage(_image),
backgroundColor: Colors.red,
),
On your signUpUser you are directly using bang! without checking null.
Here
// signup user using our authmethodds
...
file: _image!);
Try
void signUpUser() async {
if (_image == null) {
debugPrint("got null on _image");
_isLoading = false;
return;
}
And everything seems ok on Stack

Flutter Integration test case fails when run

I'm trying to run an integration test in my app. The screen is my login screen which leads to a signup flow and logged in to Home Screen. I'm using flutter integration test from the framework it self.
I've tried to run an integration test on the login screen but I get this error,
The following TestFailure object was thrown running a test:
Expected: exactly one matching node in the widget tree
Actual: _WidgetPredicateFinder:<zero widgets with widget matching predicate (Closure: (Widget) =>
bool) (ignoring offstage widgets)>
Which: means none were found but one was expected
My Login screen looks like this
class LoginScreen extends StatefulWidget {
static String tag = loginScreenRoute;
const LoginScreen({Key? key}) : super(key: key);
#override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _userLoginFormKey = GlobalKey<FormState>();
String? _userName = "";
String? _password = "";
bool _invisiblePass = false;
TextEditingController usernameController = TextEditingController();
TextEditingController passwordController = TextEditingController();
bool hasInterNetConnection = false;
late StreamSubscription _connectionChangeStream;
#override
initState() {
//Create instance
ConnectionUtil connectionStatus = ConnectionUtil.getInstance();
//Initialize
connectionStatus.initialize();
//Listen for connection change
_connectionChangeStream =
connectionStatus.connectionChange.listen(connectionChanged);
super.initState();
}
#override
void dispose() {
_connectionChangeStream.cancel();
super.dispose();
}
void connectionChanged(dynamic hasConnection) {
setState(() {
hasInterNetConnection = hasConnection;
//print(isOffline);
});
if (!hasInterNetConnection) {
offlineBar(context);
}
}
final loading = Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
CircularProgressIndicator(
color: lightWTextColor,
),
Text(" Login in ... Please wait")
],
);
void _showPassword() {
setState(() {
_invisiblePass = !_invisiblePass;
});
}
#override
Widget build(BuildContext context) {
//// user email ////
TextFormField userName() => TextFormField(
key: const Key('login username input'),
autofocus: false,
keyboardType: TextInputType.emailAddress,
controller: usernameController,
validator: validateEmail,
onSaved: (value) => _userName = value!.trim(),
textInputAction: TextInputAction.next,
style: AppTheme.body1WTextStyle,
decoration: buildInputDecoration(
'Enter Email',
Icons.email,
lightWTextColor.withOpacity(0.4),
),
// focusNode: _usernameFocusNode,
// onFieldSubmitted: (String val) {
// final focusNode = FocusNode();
// focusNode.unfocus();
// },
);
//// user password ////
TextFormField userPassword() => TextFormField(
key: const Key('login password input'),
obscureText: !_invisiblePass,
keyboardType: TextInputType.visiblePassword,
controller: passwordController,
validator: validatePassword,
onSaved: (value) => _password = value!.trim(),
textInputAction: TextInputAction.done,
style: AppTheme.body1WTextStyle,
decoration: buildInputDecoration(
'Enter Password',
Icons.vpn_lock,
lightWTextColor.withOpacity(0.4),
).copyWith(
suffixIcon: GestureDetector(
onTap: () {
_showPassword();
},
child: Icon(
_invisiblePass ? Icons.visibility : Icons.visibility_off,
color: Colors.black54,
),
),
),
);
final forgotLabel = Padding(
padding: const EdgeInsets.all(0.0),
child: Container(
alignment: Alignment.topRight,
child: TextButton(
child: const Text(
"Forgot password?",
style: AppTheme.body1WTextStyle,
),
onPressed: () {
Navigator.of(context).pushNamed(passwordResetScreenRoute);
},
),
),
);
final signupLabel = Padding(
padding: const EdgeInsets.all(10.0),
child: TextButton(
child: const Text(
"Sign Up for an Account",
style: AppTheme.subTitleWTextStyle,
),
onPressed: () {
Navigator.of(context).pushNamed(
userEditScreenRoute,
arguments: eProfile.addProfile,
);
},
),
);
final loginButton = ButtonWidget(
key: const Key('login button'),
text: 'LOG IN',
btnColor: accentColor,
borderColor: accentColor,
textColor: lightWTextColor,
onPressed: () {
Navigator.of(context).pushReplacementNamed(homeScreenRoute);
// _submit();
},
);
final loginForm = Form(
key: _userLoginFormKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
userName(),
const SizedBox(
height: 10.0,
),
userPassword(),
forgotLabel,
const SizedBox(
height: 10.0,
),
loginButton,
const SizedBox(
height: 10.0,
),
signupLabel,
],
),
);
final mainBody = InkWell(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: wBackground(),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/images/_logo.png',
height: 200.0,
),
Expanded(
flex: 1,
child: loginForm, //Text('this text here'),
),
],
),
),
),
),
);
return SafeArea(
child: Scaffold(
body: SingleChildScrollView(
child: mainBody,
),
),
);
}
}
and when I try to navigate to Home Screen on tap of login button, the test fails.
my test case is like this
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
//
// start.main();
login.main();
}
//
void main() {
doLoginTest();
}
void doLoginTest() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets("Login in test run", (WidgetTester tester) async {
//
pawfect.main();
await tester.pumpAndSettle(const Duration(seconds: 3));
//test here
final Finder login =
find.byWidgetPredicate((widget) => widget is LoginScreen);
expect(login, findsOneWidget);
await tester.pumpAndSettle(const Duration(seconds: 1));
//
var emailInput = find.byKey(const Key('login username input'));
await tester.tap(emailInput);
await tester.enterText(emailInput, "test#m.com");
await tester.pumpAndSettle(const Duration(seconds: 1));
//
var passwordInput = find.byKey(const Key('login password input'));
await tester.tap(passwordInput);
await tester.enterText(passwordInput, "password");
await tester.pumpAndSettle(const Duration(seconds: 1));
//
var loginButton = find.byKey(const Key('login button'));
await tester.tap(loginButton, warnIfMissed: false);
await tester.pumpAndSettle(const Duration(seconds: 3));
//
// expect(version, findsOneWidget);
// final Finder home = find.byWidget(const HomeScreen());
expect(find.byWidgetPredicate((widget) => widget is HomeScreen),
findsOneWidget);
// await tester.pumpAndSettle(const Duration(seconds: 1));
var version = find.byWidgetPredicate(
(widget) => widget is Text && widget.data!.contains("Version: 2.0"));
expect(version, findsOneWidget);
await tester.pumpAndSettle(const Duration(seconds: 3));
});
}
what am I doing wrong here? I tried to look for something helpful over the internet and in the docs but I couldn't get my hands dirty enough. Will someone please help me to write a fine Integration test move in with screen to another screen. Thank you so much in advance.
Do you still have both main methods in your test file? If so can you remove the first (I don't understand how that can even be there):
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
//
// start.main();
login.main();
}
Also try stepping through your code - add a breakpoint in your test file and with that test file still in the editor press F5 ( I am assuming here you are in VSCode like me ), find out which expect call is reporting the failure - I'm guessing it is the second:
expect(find.byWidgetPredicate((widget) => widget is HomeScreen),
findsOneWidget);
Try adding this code before that call (instead of your existing pumpAndSettle call):
await tester.pump(const Duration(milliseconds: 4000));
await tester.pumpAndSettle();
Also consider some the ideas in this answer: https://stackoverflow.com/a/70472212/491739

Flutter pressing back button pops up previous snackBar from Login page again

I have a LoginPage in Flutter. After login, it shows a small snackbar with "success" or "failure.." if password is wrong, then it navigates to the todo list.
When I now press the "back" button on an Android device, it navigates back to the login screen. However, there is still the snackbar popping up and saying "Login successful, redirecting..", and also, my textfields are not emptied and still have the values from the first login, why? That should not happen, but I cannot figure out why that is... here is my code:
import 'package:flutter/material.dart';
import 'package:todoey_flutter/components/rounded_button.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:todoey_flutter/util/file_handler.dart';
import 'package:provider/provider.dart';
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
String username;
String password;
String hashedPW;
// Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
var _nameController = TextEditingController();
var _pwController = TextEditingController();
#override
Widget build(BuildContext context) {
CryptOid cy = Provider.of<CryptOid>(context, listen: true);
FileHandler fh = Provider.of<FileHandler>(context, listen: true);
return Scaffold(
backgroundColor: Colors.white,
body: Builder(
builder: (BuildContext scaffoldBuildContext) {
return Container(
//inAsyncCall: isSpinning,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 34.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
/*
Flexible(
child: Hero(
tag: 'logo',
child: Container(
height: 200.0,
child: Image.asset('images/logo.png'),
),
),
),*/
SizedBox(
height: 48.0,
),
TextField(
controller: _nameController,
style: TextStyle(color: Colors.black54),
onChanged: (value) {
//Do something with the user input.
username = value.toLowerCase();
},
decoration: InputDecoration(
hintText: 'Enter your username',
),
),
SizedBox(
height: 8.0,
),
TextField(
controller: _pwController,
obscureText: true,
style: TextStyle(color: Colors.black54),
onChanged: (value) {
//Do something with the user input.
password = value;
},
decoration: InputDecoration(
hintText: 'Enter your password',
),
),
SizedBox(
height: 24.0,
),
RoundedButton(
title: 'Login',
colour: Colors.lightBlueAccent,
onPressed: () async {
Scaffold.of(scaffoldBuildContext).removeCurrentSnackBar();
print("user: $username, pw: $password");
if ((username != '' && username != null) && (password != '' && password != null)) {
SharedPreferences prefs = await SharedPreferences.getInstance();
// cy.test();
if ((username != '' && username != null) && prefs.containsKey(username)) {
hashedPW = prefs.getString(username);
bool decryptPW = await cy.deHash(hashedPW, password);
if (decryptPW) {
cy.setUsername(username);
fh.setUser(username);
prefs.setString('activeUser', username);
Scaffold.of(scaffoldBuildContext).showSnackBar(
SnackBar(
content: Text("Login successful! redirecting.."),
),
);
Navigator.pushNamed(context, 'taskScreen');
} else {
Scaffold.of(scaffoldBuildContext).showSnackBar(
SnackBar(
content: Text("Wrong password for user $username!"),
),
);
}
} else {
String hashedPW = await cy.hashPW(password);
prefs.setString('activeUser', username);
prefs.setString(username, hashedPW);
cy.setUsername(username);
fh.setUser(username);
Scaffold.of(scaffoldBuildContext).showSnackBar(
SnackBar(
content: Text("User created successful! redirecting.."),
),
);
Navigator.pushNamed(context, 'taskScreen');
//prefs.setString(username, hashedPW);
}
_nameController.clear();
_pwController.clear();
} else {
Scaffold.of(scaffoldBuildContext).showSnackBar(
SnackBar(
content: Text("User and password may not be empty.."),
),
);
_nameController.clear();
_pwController.clear();
return;
}
},
),
],
),
),
);
},
),
);
}
}
You should create a ScaffoldState GlobalKey then assign the to the scaffold.
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: Container());
}
The use the key to showSnackBar
void _showInSnackBar(String value) {
_scaffoldKey.currentState
.showSnackBar(new SnackBar(content: new Text(value)));
}
So your full code would look like this:
import 'package:flutter/material.dart';
import 'package:todoey_flutter/components/rounded_button.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:todoey_flutter/util/file_handler.dart';
import 'package:provider/provider.dart';
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
String username;
String password;
String hashedPW;
// Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
var _nameController = TextEditingController();
var _pwController = TextEditingController();
#override
Widget build(BuildContext context) {
CryptOid cy = Provider.of<CryptOid>(context, listen: true);
FileHandler fh = Provider.of<FileHandler>(context, listen: true);
return Scaffold(
key: _scaffoldKey,
backgroundColor: Colors.white,
body: Builder(
builder: (BuildContext scaffoldBuildContext) {
return Container(
//inAsyncCall: isSpinning,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 34.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
/*
Flexible(
child: Hero(
tag: 'logo',
child: Container(
height: 200.0,
child: Image.asset('images/logo.png'),
),
),
),*/
SizedBox(
height: 48.0,
),
TextField(
controller: _nameController,
style: TextStyle(color: Colors.black54),
onChanged: (value) {
//Do something with the user input.
username = value.toLowerCase();
},
decoration: InputDecoration(
hintText: 'Enter your username',
),
),
SizedBox(
height: 8.0,
),
TextField(
controller: _pwController,
obscureText: true,
style: TextStyle(color: Colors.black54),
onChanged: (value) {
//Do something with the user input.
password = value;
},
decoration: InputDecoration(
hintText: 'Enter your password',
),
),
SizedBox(
height: 24.0,
),
RoundedButton(
title: 'Login',
colour: Colors.lightBlueAccent,
onPressed: () async {
_scaffoldKey.currentState.removeCurrentSnackBar();
print("user: $username, pw: $password");
if ((username != '' && username != null) &&
(password != '' && password != null)) {
SharedPreferences prefs =
await SharedPreferences.getInstance();
// cy.test();
if ((username != '' && username != null) &&
prefs.containsKey(username)) {
hashedPW = prefs.getString(username);
bool decryptPW = await cy.deHash(hashedPW, password);
if (decryptPW) {
cy.setUsername(username);
fh.setUser(username);
prefs.setString('activeUser', username);
_showInSnackBar("Login successful! redirecting..");
Navigator.pushNamed(context, 'taskScreen');
} else {
_showInSnackBar(
"Wrong password for user $username!");
}
} else {
String hashedPW = await cy.hashPW(password);
prefs.setString('activeUser', username);
prefs.setString(username, hashedPW);
cy.setUsername(username);
fh.setUser(username);
_showInSnackBar(
"User created successful! redirecting..");
Navigator.pushNamed(context, 'taskScreen');
//prefs.setString(username, hashedPW);
}
_nameController.clear();
_pwController.clear();
} else {
_showInSnackBar("User and password may not be empty..");
_nameController.clear();
_pwController.clear();
return;
}
},
),
],
),
),
);
},
),
);
}
void _showInSnackBar(String value) {
_scaffoldKey.currentState
.showSnackBar(new SnackBar(content: new Text(value)));
}
}