Flutter Integration test case fails when run - flutter

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

Related

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

Can't build new widget on pressing button at run time in FormBuilder

I am trying to build a new widget every time a button is pressed.
I am using object of Global key to change the current state of Form as other local keys doesn't provide me the capability to change the state of FormBuilder
final _formKey = GlobalKey<FormBuilderState>();
Problem is when I try to create object of GlobalKey(_formKey) under listTile widget that I 'm trying to build on runtime, form builderr text fields don't work i.e appear and disappear instantly! But when create _formKey oustside the listTile widget under stateful widget, many other errors appears
i.e setState() called after dispose(), Global key used for multiple widgets etc.
Should I use here local keys i.e value,object or unique key? But they aren't providing me to change the current state of form builder!
Check my code:
class _addMenuState extends State<addMenu> {
var _price = TextEditingController();
var _itemName = TextEditingController();
var _desc = TextEditingController();
File _image;
String itemImageUrl;
List<menu> items = [];
final _formKey = GlobalKey<FormBuilderState>();
Future getImageFromGallery() async {
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
_image = image;
print('Image Path $_image');
});
}
Future getImageFromCamera() async {
var image = await ImagePicker.pickImage(source: ImageSource.camera);
setState(() {
_image = image;
print('Image Path $_image');
});
}
Future uploadItemOfShop(BuildContext context) async {
FirebaseStorage storage = FirebaseStorage.instance;
Reference ref = storage.ref().child("${this.widget.rr.name}'s ${_itemName.text} Price ${_price.text}" + DateTime.now().toString());
if(_image.toString()==''){
Flushbar(
title: "Menu Item Image is empty",
message: "Please Add some Image first",
backgroundColor: Colors.red,
boxShadows: [BoxShadow(color: Colors.red[800], offset: Offset(0.0, 2.0), blurRadius: 3.0,)],
duration: Duration(seconds: 3),
)..show(context);
}else{
UploadTask uploadTask = ref.putFile(_image);
uploadTask.then((res) async {
itemImageUrl = await res.ref.getDownloadURL();
});
}
/* setState(() {
print("Logo uploaded");
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Your Restaurnat Logo has Uploaded'),
duration: Duration(seconds: 3),
));
});*/
}
Widget Divido() {
return Divider(
thickness: 5,
color: Colors.black45,
height: 2,
);
}
Widget listTile(int i) {
final _formKey = GlobalKey<FormBuilderState>();
return SingleChildScrollView(
child: ListTile(
title: //Code not needed here for this question as I haven't used key in it
subtitle: FormBuilder(
key: _formKey,
child: Column(
children: [
FormBuilderTextField(
controller: _itemName,
keyboardType: TextInputType.text,
name: 'item_name',
decoration: InputDecoration(labelText: 'Enter Item name'),
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(context),
]),
),
FormBuilderTextField(
controller: _price,
name: 'price',
keyboardType: TextInputType.number,
decoration: InputDecoration(labelText: 'Enter Price'),
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(context),
]),
),
FormBuilderTextField(
maxLength: 150,
controller: _desc,
keyboardType: TextInputType.multiline,
maxLines: null,
name: 'desc',
decoration: InputDecoration(
labelText: 'Description',
),
validator: FormBuilderValidators.compose(
[
FormBuilderValidators.required(context),
],
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
RaisedButton(
child: Text(
"Save",
style: TextStyle(color: Colors.white, fontSize: 16),
),
color: Colors.red,
onPressed: () {
debugPrint("null");
menu item = menu(
_itemName.text, _price.text, _desc.text, itemImageUrl);
items.add(item);
setState(() {
_count = _count + 1;
});
},
),
RaisedButton(
child: Text(
"Reset",
style: TextStyle(color: Colors.white, fontSize: 16),
),
color: Colors.grey,
onPressed: () {
_formKey.currentState.reset();
},
)
],
),
Divido()
],
),
),
selectedTileColor: Colors.red.shade300,
),
);
}
int _count = 1;
bool _showAnotherWidget = false;
#override
Widget build(BuildContext context) {
List<Widget> children = List.generate(_count, (int i) => listTile(i));
return Scaffold(
appBar: AppBar(
title: Text("Add Menu for ${this.widget.rr.name} Shop"),
),
body: SingleChildScrollView(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: children
),
),
)
);
}
}

Flutter shared preferences issue with setInt

im trying to build an app which consists of 3 roles, student teacher and parent. I thought of implementing the authentication using auth change stream and shared preferences. ie setInt('usertype',1) for student, 2 for teacher and 3 for parent.
this is my student registration screen where if the user is successfully registered, im setting the usertype as 1, also i did the same for teacher and parent registration screen.
class StudentRegisterScreen extends StatefulWidget {
final Function toggleView;
StudentRegisterScreen({this.toggleView});
#override
_StudentRegisterScreenState createState() => _StudentRegisterScreenState();
}
class _StudentRegisterScreenState extends State<StudentRegisterScreen> {
final AuthService _authService = AuthService();
final _formkey = GlobalKey<FormState>();
final usertype = await SharedPreferences.getInstance();
//String name = '';
//String email = '';
//String password = '';
#override
Widget build(BuildContext context) {
//String _message = '';
Size size = MediaQuery.of(context).size;
return Scaffold(
backgroundColor: HexColor(studentPrimaryColour),
body: SafeArea(
child: SingleChildScrollView(
child: Form(
key: _formkey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 20.0,
),
HeadingText(
text: 'Register',
size: 60.0,
color: Colors.white,
),
Container(
height: 20.0,
child: HeadingText(
text: AuthService().message,
size: 20.0,
color: Colors.white,
),
),
SizedBox(
height: 25.0,
),
RoundedInputField(
hintText: 'Name',
validator: (val) =>
val.isEmpty ? 'Oops! you left this field empty' : null,
onChanged: (val) {
name = val;
},
),
SizedBox(
height: 5.0,
),
RoundedInputField(
hintText: 'Email',
validator: (val) => val.isEmpty ? 'enter an email' : null,
onChanged: (val) {
email = val;
},
),
SizedBox(
height: 5.0,
),
RoundedInputField(
hintText: 'Password',
validator: (val) =>
val.isEmpty ? 'atleast provide a password' : null,
boolean: true,
onChanged: (val) {
password = val;
}),
SizedBox(
height: 5.0,
),
Container(
margin: EdgeInsets.symmetric(vertical: 10),
width: size.width * 0.8,
child: ClipRRect(
borderRadius: BorderRadius.circular(29),
child: FlatButton(
padding:
EdgeInsets.symmetric(vertical: 20, horizontal: 40),
color: Colors.white,
onPressed: () async {
//
// if (_formkey.currentState.validate()) {
// print(email);
// print(password);
usertype.setInt('usertype',1);
// dynamic result =
// await _authService.registerWithEmailpasswd(
// email,
// password,
// name,
// );
//
},
child: HeadingText(
color: HexColor(studentPrimaryColour),
text: 'Register',
size: 12.0,
),
),
),
),
SizedBox(
height: 15.0,
),
InkWell(
onTap: () {
// Navigator.pop(context);
widget.toggleView();
},
child: HeadingText(
text: 'Already registered?',
color: Colors.white,
size: 10,
),
),
],
),
),
),
),
);
}
}
but im getting an error
I/flutter (13157): 0
E/flutter (13157): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: NoSuchMethodError: The method 'setInt' was called on null.
E/flutter (13157): Receiver: null
E/flutter (13157): Tried calling: setInt("usertype", 1)
this below is my wrapper class right now, I thought of printing the values on to the console before proceeding further, i will be implementing switch cases for showing the different user screens
class Welcomescreen extends StatefulWidget {
#override
_WelcomescreenState createState() => _WelcomescreenState();
}
class _WelcomescreenState extends State<Welcomescreen> {
SharedPreferences userDetails;
int usertype;
#override
void initState() {
// TODO: implement initState
super.initState();
checkUserType();
}
void checkUserType() async {
userDetails = await SharedPreferences.getInstance();
setState(() {
usertype = userDetails.getInt('usertype') ?? 0;
});
print(usertype);
}
#override
Widget build(BuildContext context) {
//final user = Provider.of<UserModel>(context);
return Body();
}
}
userType is referencing a future value SharedPreference.getInstance(), by creating final userType = await SharedPrefence.getInstance() you're telling the code that calling userType will return a Future of SharedPrefence.getInstance(), is not resolving to the SharedPreference class until you actually await for it (using await is not doing anything because you're not really inside an asyn function)
try calling it like this:
onPressed: () async {
(await usertype).setInt('usertype',1);
// or maybe (userType).setInt('usertype',1);
}
userType will be replaced for the value you're referencing (await SharedPreference.getInstance())
for the program it looks like this:
onPressed: () async {
(await SharedPreference.getInstance()).setInt('usertype',1);
}
first it needs to await the furure, after that you can actually call its methods

How to start something automatically in flutter

I have the following code that initially displays a blank page, but I'd like an rAlert dialog to display automatically. Once the user clicks 'Request' or 'Cancel', some text will be displayed on the screen.
But I can't get the code to run that displays the Alert. I had it working by showing a button and clicking the button, but i need the Alert to display automatically when the page is displayed. I tried putting it in the initState. I didn't get any errors, but it didn't work either.
Anyone know what I need to do? Thanks?
import 'package:flutter/material.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
import 'dart:async';
import 'package:rostermeon/cwidgets/general_widgets.dart';
import 'package:rostermeon/rmo_constants.dart';
class ForgotPassword extends StatefulWidget {
static const String id = 'forgot_password_screen';
#override
_ForgotPasswordState createState() => _ForgotPasswordState();
}
class _ForgotPasswordState extends State<ForgotPassword> {
StreamController<bool> _events;
#override
initState() {
super.initState();
_events = new StreamController<bool>();
doRequest(context: context);
}
Future<bool> doSaveRequest({String pReason}) async {
await Future.delayed(const Duration(seconds: 3), () {});
return false;
}
Future<bool> doRequest({context}) {
String _reason = '';
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
TextEditingController reasonController = TextEditingController();
TextStyle _style = TextStyle(fontFamily: 'Montserrat', fontSize: 18.0, fontWeight: FontWeight.normal);
InputDecoration _textFormFieldDecoration({String hintText, double padding}) => InputDecoration(
//contentPadding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 8.0),
contentPadding: EdgeInsets.all(padding),
isDense: true,
hintText: hintText,
hintStyle: TextStyle(color: kHintText),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(5)),
),
);
return Alert(
context: context,
title: 'Request New Password',
content: StreamBuilder<bool>(
initialData: false,
stream: _events.stream,
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
print(" ${snapshot.data.toString()}");
return snapshot.data
? CircularProgressIndicator()
: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 20.0),
Text('Email', textAlign: TextAlign.left, style: _style),
SizedBox(height: 10.0),
TextFormField(
validator: (value) {
if (value.isEmpty) {
return "please enter email";
}
return null;
},
onSaved: (value) {
_reason = value;
},
decoration: _textFormFieldDecoration(
hintText: 'your email address',
padding: 8.0,
),
controller: reasonController,
),
SizedBox(height: 10.0),
],
),
);
}),
buttons: [
DialogButton(
child: Text('Request', style: TextStyle(color: Colors.white, fontSize: 20)),
color: kMainColor,
onPressed: () async {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
print(_reason);
_events.add(true);
var saved = await doSaveRequest(pReason: _reason);
if (saved) {
Navigator.pop(context, false);
} else {
_events.add(false);
}
Navigator.of(context).pop();
// Navigator.pop(context, false);
}
},
),
DialogButton(
child: Text('Cancel', style: TextStyle(color: Colors.white, fontSize: 20)),
color: kMainColor,
onPressed: () {
Navigator.pop(context, false);
},
),
],
).show();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: rmoAppBar(subText: 'Request New Password', hideBackButton: false),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[],
),
),
);
}
}
Dialogs/Alerts need the buildContext in order to work, You can't have the buildContext before build() method is called, that's why you can't just call it in initstate() before the build is called.
To make it work use addPostFrameCallback to make sure it delays until widget is built:
void initState() {
super.initState();
WidgetsBinding.instance
.addPostFrameCallback((_) => yourMethod(context));
}
https://www.didierboelens.com/2019/04/addpostframecallback/