I made a pin verification with countdown using the timer_count_down package. What I'm trying to do is when the countdown is finished, it shows a text saying "Resend PIN" to resend the PIN to email and starts the countdown again.
Here is my Login.dart code snippet when I enter my email address it sends a PIN code to my email :
...
Form(
key: _formKey,
child: TextFormField(
cursorColor: Color(0xFF2481CF),
autofocus: true,
validator: (value) {
if (value == null || value.isEmpty || !EmailValidator.validate(value.trim())) {
return 'Email is not valid!';
}
return null;
},
controller: _controller,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Color(0xFF2481CF)),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Color(0xFF2481CF)),
),
labelText: 'Enter your email :',
labelStyle: TextStyle(
color: Color(0xFF2481CF)
)
),
style: TextStyle(
fontSize: 19.0,
height: 1.3,
),
),
),
...
floatingActionButton: FloatingActionButton(
// onPressed: _submit,
onPressed: () {
if (_formKey.currentState!.validate()) {
showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 20,),
Text('PIN code will be sent to'),
SizedBox(height: 10,),
Text(_controller.text,
style: TextStyle(
fontWeight: FontWeight.bold
),),
SizedBox(
height: 30,
),
Text('Is this OK or would you like to edit the email address?'),
],
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'Cancel'),
child: const Text('Edit',
style: TextStyle(
color: Color(0xFF2481CF)
),),
),
TextButton(
onPressed: () {
//kirim email
sendEmail(_controller.text.trim());
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => PinVerification(_controller.text.trim())),
);
},
child: const Text('Verify',
style: TextStyle(
color: Color(0xFF2481CF)
),),
),
],
),
);
};
},
child: const Icon(Icons.send),
),
And for the PinVerification.dart :
Widget onlySelectedBorderPinPut() {
final BoxDecoration pinPutDecoration = BoxDecoration(
color: const Color.fromRGBO(235, 236, 237, 1),
borderRadius: BorderRadius.circular(5.0),
);
return Form(
key: _formKey,
child: Column(
children: [
SizedBox(
height: 50,
),
Text(
'Enter 6 digit code we sent to your email.',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey),
),
SizedBox(
height: 30,
),
GestureDetector(
onLongPress: () {
print(_formKey.currentState?.validate());
},
child: PinPut(
validator: (s) {
if (s != null && s.contains('1')) return null;
return 'NOT VALID';
},
useNativeKeyboard: true,
autovalidateMode: AutovalidateMode.always,
withCursor: true,
fieldsCount: 6,
fieldsAlignment: MainAxisAlignment.spaceAround,
textStyle: const TextStyle(fontSize: 25.0, color: Colors.black),
eachFieldMargin: EdgeInsets.all(0),
eachFieldWidth: 45.0,
eachFieldHeight: 55.0,
onSubmit: (String pin) => postRequest(pin),
focusNode: _pinPutFocusNode,
controller: _pinPutController,
submittedFieldDecoration: pinPutDecoration,
selectedFieldDecoration: pinPutDecoration.copyWith(
color: Colors.white,
border: Border.all(
width: 2,
color: const Color.fromRGBO(160, 215, 220, 1),
),
),
followingFieldDecoration: pinPutDecoration,
pinAnimationType: PinAnimationType.scale,
),
),
SizedBox(
height: 20,
),
Countdown(
seconds: 20,
build: (_, double time) =>
RichText(
text: TextSpan(
text: 'Send PIN again in ',
style: TextStyle(
color: Theme.of(context).inputDecorationTheme.labelStyle?.color
),
children: <TextSpan>[
TextSpan(
text: time.toString(),
style: TextStyle(
color: Color(0xFF2481CF),
fontWeight: FontWeight.bold
)),
TextSpan(text: ' sec',
style: TextStyle(
color: Theme.of(context).inputDecorationTheme.labelStyle?.color
))
]
),
),
onFinished: () {
},
),
],
),
);
}
This is the screenshot for my PinVerification.dart:
Any solutions?
Created custom timer It worked for me
late Timer _timer;
int _start = 60;
void startTimer() {
const oneSec = const Duration(seconds: 1);
_timer = new Timer.periodic(
oneSec,
(Timer timer) {
if (_start == 0) {
setState(() {
timer.cancel();
});
} else {
setState(() {
_start--;
});
}
},
);
}
#override
void initState() {
super.initState();
startTimer();
}
#override
void dispose() {
super.dispose();
_timer.cancel();
errorController!.close();
}
And inside widget check condition as follows:
_start != 0
? Row(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
"Resend Code in",
style: TextStyle(
color: Colors.white.withOpacity(0.5),
fontWeight: FontWeight.bold,
fontSize: 16),
),
const SizedBox(width: 10),
Text(
_start.toString(),
style: TextStyle(
color: MyTheme.yellow,
fontWeight: FontWeight.bold,
fontSize: 20),
),
],
)
: YourWidget() // your widget here
When the Resend Otp is clikced reset the timer is as follows:
setState(() {
_start = 60;
startTimer();
});
Related
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:keyboard_dismisser/keyboard_dismisser.dart';
import 'package:money_formatter/money_formatter.dart';
import 'package:shukela_app/api/banking_app_api.dart';
import 'package:shukela_app/screens/stokvel_detail.dart';
import 'package:shukela_app/screens/stokvels.dart';
import 'package:shukela_app/utils/constants.dart';
import 'package:shukela_app/utils/global_variables.dart';
import 'package:sizer/sizer.dart';
import 'package:shukela_app/model/bankingapp_model.dart';
import '../utils/user_preferences.dart';
class PayMultipleStokvelScreen extends StatefulWidget {
const PayMultipleStokvelScreen({Key? key}) : super(key: key);
#override
State<PayMultipleStokvelScreen> createState() =>
_PayMultipleStokvelScreenState();
}
TextEditingController txtSearch = TextEditingController();
TextEditingController txtAmount = TextEditingController();
class _PayMultipleStokvelScreenState extends State<PayMultipleStokvelScreen> {
String? selectedType;
bool hasText = false;
String buttonText = "PAY NOW";
bool isLoading = false;
Widget? showHideIcon() {
if (hasText) {
return IconButton(
icon: const Icon(
Icons.clear,
color: AppColors.primaryBlue,
),
onPressed: () {
txtSearch.clear();
setState(() {
hasText = false;
});
},
);
} else {
return null;
}
}
// void _showMultiSelectDialog(BuildContext context) async {
// await showDialog(
// context: context,
// builder: (ctx) {
// return MultiSelectDialog(
// items: _animals.map((e) => MultiSelectItem(e, e)).toList(),
// initialValue: _selectedAnimals,
// onConfirm: (values) {...},
// );
// },
// );
// }
double? balance;
final _formKey = GlobalKey<FormState>();
var selectedValue;
List<StokvelDetail> selectedStokvel = [];
#override
void initState() {
super.initState();
balance = double.parse(UserPreferences.getBalance() ?? '');
}
mf() {
MoneyFormatter mf = MoneyFormatter(amount: balance!);
return mf;
}
StokvelListState currentState = StokvelListState.showAllListState;
#override
Widget build(BuildContext context) => KeyboardDismisser(
gestures: const [GestureType.onTap],
child: SafeArea(
child: Scaffold(
backgroundColor: AppColors.secondaryColor,
appBar: AppBar(
backgroundColor: AppColors.secondaryColor,
elevation: 0,
title: const Text('Pay Multiple Stokvel',
style: screenTitleTextStyle),
leading: IconButton(
icon: const Icon(
Icons.arrow_back_ios,
color: AppColors.primaryBlue,
),
onPressed: () => Navigator.pop(context),
),
),
body: Form(
key: _formKey,
child: Column(
children: [
SizedBox(height: 5.h),
Container(
height: 6.h,
width: 98.w,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: TypeAheadFormField<Stokvel?>(
debounceDuration: const Duration(milliseconds: 500),
hideSuggestionsOnKeyboardHide: false,
suggestionsBoxDecoration: const SuggestionsBoxDecoration(
constraints: BoxConstraints(maxHeight: 450),
color: AppColors.secondaryColor,
borderRadius: BorderRadius.all(Radius.circular(10))),
textFieldConfiguration: TextFieldConfiguration(
style: const TextStyle(
color: AppColors.primaryBlue,
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
controller: txtSearch,
onChanged: (value) {
setState(() {
hasText = true;
});
},
decoration: InputDecoration(
prefixIcon: const Icon(
Icons.search,
color: AppColors.primaryBlue,
),
suffixIcon: showHideIcon(),
hintText: 'Search Stokvel',
border: const OutlineInputBorder(
borderSide:
BorderSide(color: AppColors.primaryBlue),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(4.0),
borderSide:
const BorderSide(color: AppColors.primaryBlue),
),
),
),
validator: (val) {
if (val!.isEmpty) {
return "Please select stokvel";
}
return null;
},
suggestionsCallback: BankingAppApi.getStokvelSuggestions,
itemBuilder: (context, Stokvel? suggestion) {
final stokvel = suggestion!;
//
return ListTile(
trailing: SizedBox(
height: 20.0,
width: 20.0,
child: Container(
color: AppColors.primaryBlue,
),
),
title: Text(
stokvel.stokvelName!,
style: const TextStyle(
fontFamily: Fonts.primaryFont,
fontWeight: FontWeight.bold,
color: AppColors.primaryBlue,
),
),
subtitle: Text(
stokvel.stokvelType!,
style: const TextStyle(
fontFamily: Fonts.primaryFont,
fontWeight: FontWeight.bold,
color: AppColors.primaryBlue),
),
);
},
noItemsFoundBuilder: (context) => const SizedBox(
height: 60,
child: Center(
child: Text(
'No Stokvel Found.',
style: TextStyle(fontSize: 20),
),
),
),
onSuggestionSelected: (Stokvel? suggestion) {
final stokvel = suggestion!;
setState(() {
txtSearch.text = stokvel.stokvelName!;
hasText = true;
});
stokvelID = stokvel.stokvelID;
memberID = stokvel.memberID;
},
),
),
SizedBox(
height: 4.h,
),
SizedBox(height: 3.h),
Container(
height: 6.h,
width: 98.w,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: TextFormField(
controller: txtAmount,
decoration: const InputDecoration(
labelStyle: TextStyle(
color: AppColors.primaryBlue,
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
suffixText: "ZAR",
border: OutlineInputBorder(
borderSide:
BorderSide(color: AppColors.primaryBlue)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1, color: AppColors.primaryBlue),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1, color: AppColors.primaryBlue),
),
labelText: 'Amount',
contentPadding:
EdgeInsets.only(left: 20, right: 15, bottom: 8),
),
keyboardType: TextInputType.number,
style: const TextStyle(
fontSize: 20.0,
fontFamily: Fonts.primaryFont,
color: AppColors.primaryBlue),
validator: (val) {
if (val!.isEmpty) {
return "Please enter amount";
}
return null;
},
),
),
SizedBox(height: 3.h),
Container(
height: 6.h,
width: 98.w,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: TextField(
readOnly: true,
style: const TextStyle(
fontSize: 18.0, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: "Wallet Balance : R " + mf().output.nonSymbol,
border: const OutlineInputBorder(
borderSide:
BorderSide(color: AppColors.primaryBlue),
borderRadius: BorderRadius.horizontal()),
focusedBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1, color: AppColors.primaryBlue),
),
enabledBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1, color: AppColors.primaryBlue),
),
),
),
),
SizedBox(height: 3.h),
Container(
height: 50,
width: 400,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.white)),
child: isLoading
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
CircularProgressIndicator(
color: AppColors.secondaryColor),
SizedBox(width: 24),
Text(
"Submitting payment...",
style: TextStyle(
color: AppColors.secondaryColor),
)
],
)
: Text(
buttonText,
style: const TextStyle(
fontFamily: Fonts.primaryFont,
fontWeight: FontWeight.bold,
color: AppColors.primaryBlue),
),
onPressed: () {
if (_formKey.currentState!.validate()) {
if (double.parse(txtAmount.text) <= balance!) {
setState(
() {
isLoading = true;
},
);
stokvelTransact.amount =
double.parse(txtAmount.text);
stokvelTransact.memberID = memberID;
stokvelTransact.stokvelID = stokvelID;
stokvelTransact.transactionTypeID = 1;
api
.stokvelDeposit(stokvelTransact,
"StokvelTransaction/StokvelTransact")
.then(
(value) => setState(
() {
Future.delayed(
const Duration(seconds: 3));
isLoading = false;
if (value == "Success") {
ScaffoldMessenger.of(context)
.showSnackBar(snackBar(
content:
'Payment made succesfully',
duration: 5));
} else {
ScaffoldMessenger.of(context)
.showSnackBar(snackBar(
content:
'We have encountered technical problems, Try again later',
duration: 5));
}
},
),
)
.catchError(
(err) {
setState(() {
isLoading = false;
});
ScaffoldMessenger.of(context).showSnackBar(
snackBar(
content: err.toString(), duration: 7));
},
);
} else {
ScaffoldMessenger.of(context).showSnackBar(snackBar(
content: "Insuficient funds!", duration: 7));
}
}
},
),
),
],
),
),
),
),
);
}
The above code is for the single page that I want to achieve this
This is what I want to achieve or any suggestions are accepted, I want to mark the Group of Stokvels I want to pay and pay them different accounts I don't know if it's possible please I'm new in flutter it a project I have to get it done as soon as possible this is the source code for my whole payment page, for now, I'm able to pay a single Stokvel Group but I want to select multiple
I highly suggest starting with formatting of the current code - its hard to read and with that hard to maintain.
The simple idea would be:
use List<StokvelDetail> selectedStokvel = []; you created to add the Stokvel's in every time you select Stokvel in SearchBar
Add Map<int,double> amounts = {}; <StokvelId, amount> to keep track of the amount you want to send to each Stokvel
Based on selectedStokvel build multiple 'amount window' widgets
Before submit verify walletBalance >= amounts.values.reduce((a,b)=> a + b)
For each selectedStokvel call api.stokvelDeposit(stokvelTransact, ...
I would primarly focus on spliting this code to multiple Widget classes, there are many schools but I like to have around 150 lines of code in single file at most.
From what i see your file can be easily split into:
Column(
children: [
SizedBox(),
SearchBar(),
SizedBox(),
AmountInput(), --> StokvelAmountInput() for each selectedStokvel
SizedBox(),
WalletBalance(),
SizedBox(),
SubmitButton(),
]),
```
I have been implementing a search bar in Flutter, which works as expected when searching according to usernames, but does not display all the users when it is empty.
It only displays all the users when typing something in the search bar and completely backspacing it. The following code snippet shows its implementation:
class UsersTable extends StatefulWidget {
List<User> users;
List<Library> posts;
UsersTable(
this.users,
this.posts, {
Key? key,
}) : super(key: key);
#override
State<UsersTable> createState() => _UsersTableState();
}
class _UsersTableState extends State<UsersTable> {
List<User> _foundUsers = [];
#override
initState() {
// at the beginning, all users are shown
Future.delayed(Duration.zero, () async {
_foundUsers = widget.users;
print(_foundUsers.length);
if (mounted) setState(() {});
});
super.initState();
}
// This function is called whenever the text field changes
void _runFilter(String enteredKeyword) {
List<User> results = [];
if (enteredKeyword.trim().isEmpty) {
// if the search field is empty or only contains white-space, we'll display all users
results = widget.users;
print("if filter length");
print(results.length);
} else {
results = widget.users
.where((user) => user.username
.toLowerCase()
.contains(enteredKeyword.toLowerCase()))
.toList();
// we use the toLowerCase() method to make it case-insensitive
}
// Refresh the UI
setState(() {
_foundUsers = results;
});
}
Widget body(BuildContext context, List<User> users, List<Library> posts) {
return SafeArea(
child: Material(
child: Container(
padding: const EdgeInsets.all(defaultPadding),
decoration: const BoxDecoration(
color: boxColor,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Align(
alignment: Alignment.center,
child: Text(
"Active Users",
//style: Theme.of(context).textTheme.subtitle1,
style: TextStyle(
color: Colors.white,
fontSize: 20,
decoration: TextDecoration.none),
)),
const SizedBox(
height: 30,
),
TextField(
style: const TextStyle(color: Colors.white),
onChanged: (value) => _runFilter(value),
decoration: const InputDecoration(
labelStyle: TextStyle(color: Colors.white38),
labelText: 'Search',
suffixIcon: Icon(Icons.search),
suffixIconColor: Colors.white),
),
const SizedBox(
height: 20,
),
// SizedBox(
//width: double.infinity,
// child: SingleChildScrollView(
Container(
child: DataTable(
columnSpacing: 7,
columns: const [
// DataColumn(
// label: Text(
// "First Name",
// style: TextStyle(color: Colors.white),
// ),
// ),
// DataColumn(
// label: Text(
// "Last Name",
// style: TextStyle(color: Colors.white),
// ),
// ),
DataColumn(
label: Text(
"Username",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
),
DataColumn(
label: Text(
"Email ID",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
),
// DataColumn(
// label: Text(
// "DOB",
// style: TextStyle(color: Colors.white),
// ),
// ),
DataColumn(
label: Text(
"Account\nType",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
),
DataColumn(
label: Text(
"Delete\nUser",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
),
DataColumn(
label: Text(
"View\nPosts",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
),
],
rows:
//_foundUsers.isNotEmpty?
_foundUsers
.map(
(user) => DataRow(
cells: <DataCell>[
// DataCell(Text(user.first_name,
// style: TextStyle(color: Colors.white))),
// DataCell(Text(user.last_name,
// style: TextStyle(color: Colors.white))),
DataCell(Text(user.username,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white))),
DataCell(Text(user.email,
style: TextStyle(color: Colors.white))),
// DataCell(Text(user.date_of_birth,
// style: TextStyle(color: Colors.white))),
DataCell(Text(user.account_type,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white))),
DataCell(
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.purple,
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 15),
textStyle: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold),
shadowColor: Colors.grey,
),
onPressed: () {
// if (user.username
// .compareTo('hwumazijadmin48') !=
// 0) {
BlocProvider.of<AuthenticationBloc>(context)
.add(DeleteUser(
username: user.username));
// } else {
// const Text("Cannot Delete Admin");
// }
},
child: const Text('X'),
),
),
DataCell(
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.purple,
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 15),
textStyle: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold),
shadowColor: Colors.grey,
),
onPressed: () {
List<Library> po = [];
int i = 0;
while (i < posts.length) {
if (posts[i]
.user
.compareTo(user.username) ==
0) {
po.add(posts[i]);
}
i++;
}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PostsTable(po),
),
);
},
child: const Text('\u{1F441}'),
),
),
],
),
)
.toList()
//: []
),
),
// ),
],
),
)
//)
));
}
Am I missing something in the code?
move super.initState(); above and remove Future.delayed.
#override
initState() {
super.initState();
_foundUsers = widget.users;
}
no need to setState({}); because build method will trigger after initState. Also no need for mounted because we removed asyncoperation.
Hi in the below code when I enter my mobile number, password and then click on the login button nothing is happening. My API working in Postman is not working here.
When I press the button it is not working, Entering a valid mobile number and password are not working.
Can anyone help me to find where I did any mistakes?
Login_screen.dart:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:sample_live/home_screen.dart';
import 'package:sample_live/model/login_model.dart';
import 'package:sample_live/splash_screen.dart';
import 'package:sample_live/login_otp.dart';
import 'ProgressHUD.dart';
import 'api/api_service.dart';
class LoginScreen extends StatefulWidget {
String name;
LoginScreen({this.name});
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final mobileController = TextEditingController();
final passwordController = TextEditingController();
LoginRequestModel requestModel;
bool isApiCallProcess = false;
GlobalKey<FormState> globalFormKey = GlobalKey<FormState>();
LoginRequestModel loginRequestModel;
final scaffoldKey = GlobalKey<ScaffoldState>();
#override
void initState(){
super.initState();
requestModel=new LoginRequestModel();
}
#override
Widget build(BuildContext context) {
return ProgressHUD(
child: _uiSetup(context),
inAsyncCall: isApiCallProcess,
opacity: 0.3,
);
}
Widget _uiSetup(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Login", style: TextStyle(color: Colors.white)),
centerTitle: true,
),
body:
Stack(
children: [
Padding(
padding: EdgeInsets.all(30),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/images/hand.png',),
Padding(
padding: EdgeInsets.all(10),
child: Text("Welcome Doctor! ",
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.bold
),),
),
Padding(
padding: EdgeInsets.all(10),
),
Text("Let's treat everyone great",
style: TextStyle(
color: Colors.black,
fontSize: 15,
),),
Padding(
padding: EdgeInsets.all(10),
),
TextFormField(
minLines: 1,
keyboardType: TextInputType.number,
onSaved: (input) => loginRequestModel.Mobile = input,
textInputAction: TextInputAction.next,
decoration: InputDecoration(
labelText: "Enter Mobile No.",
hintText: "Enter Mobile No.",
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(16.0)))),
),
SizedBox(
height: 10,
),
TextFormField(
onSaved: (input) =>
loginRequestModel.Password = input,
validator: (input) =>
input.length < 3
? "Password should be more than 3 characters"
: null,
minLines: 1,
obscureText: true,
keyboardType: TextInputType.text,
textInputAction: TextInputAction.next,
decoration: InputDecoration(
labelText: "Password",
hintText: "Password",
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(16.0)))),
),
SizedBox(
height: 10,
),
Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 20, horizontal: 20),
margin: EdgeInsets.symmetric(vertical: 20, horizontal: 20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30)
),
child: RaisedButton(
color: Color(0xFF0769AA),
onPressed: () {
if (validateAndSave()) {
print(loginRequestModel.toJson());
setState(() {
isApiCallProcess = true;
});
APIService apiService = new APIService();
apiService.login(loginRequestModel).then((value) {
if (value != null) {
setState(() {
isApiCallProcess = false;
});
if (value.Status.isNotEmpty) {
final snackBar = SnackBar(
content: Text("Login Successful"));
scaffoldKey.currentState
.showSnackBar(snackBar);
} else {
final snackBar =
SnackBar(content: Text(value.Message));
scaffoldKey.currentState
// ignore: deprecated_member_use
.showSnackBar(snackBar);
}
}
});
}
},
child: Text(
"Login",
style: TextStyle(color: Colors.white),
),
),
),
SizedBox(
height: 10,
),
Text("Or",
style: TextStyle(
color: Colors.black,
fontSize: 15,
),),
Container(
width: double.infinity,
child: FlatButton(
color: Color(0xFF0769AA),
onPressed: () {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => LoginOtp("Welcome")),
(route) => false);
},
child: Text(
"Login With OTP",
style: TextStyle(color: Colors.white),
),
),
),
SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: () {
// write your function
Navigator.push(
context,
MaterialPageRoute(
builder: (contex) => SplashScreen()));
},
child: Text(
"Forgot Password",
style: TextStyle(
color: Colors.blue,
fontSize: 16,
fontWeight: FontWeight.bold,
)
)),
],
),
],
),
),
Container(
alignment: Alignment.bottomCenter,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
"By logging in or signing up, you agree to the",
textAlign: TextAlign.end,
style: TextStyle(
color: Colors.black,
fontSize: 12,
),
),
Text(
"Privacy Policy & Terms and Condition",
textAlign: TextAlign.end,
style: TextStyle(
color: Colors.blue,
fontSize: 12,
),
),
],
)
)
]),
);
}
bool validateAndSave() {
final form = globalFormKey.currentState;
if (form.validate()) {
form.save();
return true;
}
return false;
}
}
I have some trouble with hiding and showing widget. So, I have an OTP verification page to verify the code that sent to the user. In that page, when a user click a text button to resend otp code, the button will be hidden and replaced by countdown timer.
showCountdown ?
Center(
child: CountdownTimer(
textStyle: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
endTime: endTime,
onEnd: () {
setState(() {
showCountdown = false;
});
},
),
)
:
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Tidak Menerima Kode Verifikasi? ",
style: TextStyle(color: Colors.black54, fontSize: 15),
),
TextButton(
onPressed: () {
// userService.resendOtp(widget.phoneNumber.toString()).then((value) => snackBar(value));
setState(() {
showCountdown = true;
clicked++;
});
},
child: Text(
"Kirim Ulang",
style: TextStyle(
color: Constant.color,
fontWeight: FontWeight.bold,
fontSize: 16),
)
)
],
),
First click is okay, but when clicked for second time, it give this error
The following assertion was thrown building NotificationListener<KeepAliveNotification>:
setState() or markNeedsBuild() called during build.
It shows that the error maybe on this part
onEnd: () {
setState(() {
showCountdown = false;
});
},
Full Code for build method
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () {},
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/Background.png"),
alignment: Alignment.center,
fit: BoxFit.cover
)
),
child: ListView(
children: <Widget>[
// SizedBox(height: 30),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Text(
'Verifikasi Nomor HP',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 22),
textAlign: TextAlign.center,
),
),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 30.0, vertical: 8),
child: RichText(
text: TextSpan(
text: "Kode Dikirim Ke Nomor ",
children: [
TextSpan(
text: "${widget.phoneNumber}",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 15)),
],
style: TextStyle(color: Colors.black54, fontSize: 15)),
textAlign: TextAlign.center,
),
),
SizedBox(
height: 20,
),
Form(
key: formKey,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0, horizontal: 30),
child: PinCodeTextField(
appContext: context,
pastedTextStyle: TextStyle(
color: Constant.color,
fontWeight: FontWeight.bold,
),
length: 6,
obscureText: false,
obscuringCharacter: '*',
blinkWhenObscuring: true,
animationType: AnimationType.fade,
validator: (v) {
if (v!.length < 6) {
return "Harap Masukan Kode OTP Yang Benar";
} else {
return null;
}
},
pinTheme: PinTheme(
shape: PinCodeFieldShape.box,
borderRadius: BorderRadius.circular(5),
fieldHeight: 50,
fieldWidth: 40,
activeFillColor: Colors.white,
),
cursorColor: Colors.black,
animationDuration: Duration(milliseconds: 300),
errorAnimationController: errorController,
controller: textEditingController,
keyboardType: TextInputType.number,
boxShadows: [
BoxShadow(
offset: Offset(0, 1),
color: Colors.black12,
blurRadius: 10,
)
],
onCompleted: (v) {
print("Completed");
},
onChanged: (value) {
print(value);
setState(() {
currentText = value;
});
},
enablePinAutofill: true,
beforeTextPaste: (text) {
print("Allowing to paste $text");
//if you return true then it will show the paste confirmation dialog. Otherwise if false, then nothing will happen.
//but you can show anything you want here, like your pop up saying wrong paste format or etc
return true;
},
)),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: Text(
hasError ? "*Please fill up all the cells properly" : "",
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w400),
),
),
SizedBox(
height: 20,
),
showCountdown ?
Center(
child: CountdownTimer(
textStyle: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
endTime: endTime,
onEnd: () {
setState(() {
showCountdown = false;
});
},
),
)
:
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Tidak Menerima Kode Verifikasi? ",
style: TextStyle(color: Colors.black54, fontSize: 15),
),
TextButton(
onPressed: () {
// userService.resendOtp(widget.phoneNumber.toString()).then((value) => snackBar(value));
setState(() {
showCountdown = true;
clicked++;
});
},
child: Text(
"Kirim Ulang",
style: TextStyle(
color: Constant.color,
fontWeight: FontWeight.bold,
fontSize: 16),
)
)
],
),
SizedBox(
height: 14,
),
Container(
margin:
const EdgeInsets.symmetric(vertical: 16.0, horizontal: 30),
child: ButtonTheme(
height: 50,
child: TextButton(
onPressed: () {
formKey.currentState!.validate();
// conditions for validating
if (currentText.length != 6 ) {
errorController!.add(ErrorAnimationType.shake); // Triggering error shake animation
setState(() => hasError = true);
} else {
userService.verifyOtp(widget.phoneNumber.toString(), currentText).then((value) {
if(value == "Kode OTP Salah") {
errorController!.add(ErrorAnimationType.shake);
textEditingController.clear();
setState(() {
hasError = true;
snackBar(value);
});
} else {
setState(() {
hasError = false;
snackBar(value);
});
Future.delayed(Duration(seconds: 3)).then( (value) =>
Navigator.of(context).pushReplacement(
new MaterialPageRoute(builder: (context) => new SignIn()))
);
}
});
}
},
child: Center(
child: Text(
"Verifikasi Kode OTP".toUpperCase(),
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold),
)),
),
),
decoration: BoxDecoration(
color: Constant.color,
borderRadius: BorderRadius.circular(5),
boxShadow: [
BoxShadow(
color: Colors.green.shade200,
offset: Offset(1, -2),
blurRadius: 5),
BoxShadow(
color: Colors.green.shade200,
offset: Offset(-1, 2),
blurRadius: 5)
]),
),
],
),
),
),
);
}
I dont have any idea why this is happened, so I need help for this. Thank you.
This error means that the Flutter Engine is instructed to build something (using setState for example), but at the very moment another build is already in progress.
So you have to find out which of your setState statements causes this error, it could be the one at onEnd, you can debug your code to be sure. Than you can add a callback, which will be executed after the current build is completed:
WidgetsBinding.instance.addPostFrameCallback((_){
setState(() {
});
});
If you haven't already done, include this in your main function:
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp()); // or whatever you app class name is
}
On my login screen : user is asked to type in login and password. I save these value in two variables : email and password. Then the user must tap a button to actually log in.
The problem is that for some reason (that I really can't figure out...) email and password are always empty when user hits the button....
Here's the full code :
class SeConnecterScreen extends StatelessWidget {
static const String id = 'se_connecter_screen';
#override
Widget build(BuildContext context) {
var uD = Provider.of<UdProvider>(context);
String email = '';
String passWord = '';
final Connectivity _connectivity = Connectivity();
var scaffold = Scaffold(
extendBody: true,
extendBodyBehindAppBar: true,
backgroundColor: Colors.indigo[900],
body: Column(
children: [
Expanded(
flex: 4,
child: Container(
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('images/start2.jpg'), fit: BoxFit.cover),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(50),
),
),
height: MediaQuery.of(context).size.height / 4,
),
),
Expanded(
flex: 6,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
height: MediaQuery.of(context).size.height -
(MediaQuery.of(context).size.height / 4),
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
width: 1.0,
color: Colors.white,
),
borderRadius: BorderRadius.circular(10.0),
),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Text(
'WORD CHAMPIONS',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.indigo[900],
fontSize: 28.0,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 50,
),
TextField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
focusColor: Colors.white,
border: OutlineInputBorder(),
labelText: 'Tape ton adresse email'),
onChanged: (value) {
email = value;
print(email);
},
),
SizedBox(
height: 25.0,
),
TextField(
obscureText: true,
keyboardType: TextInputType.name,
decoration: InputDecoration(
focusColor: Colors.white,
border: OutlineInputBorder(),
labelText: 'Tape ton mot de passe'),
onChanged: (value) {
passWord = value;
print(passWord);
},
),
SizedBox(
height: 35.0,
),
ElevatedButton.icon(
label: Text('Se connecter'),
icon: Icon(Icons.library_add_check_rounded),
style: ElevatedButton.styleFrom(
minimumSize: Size(30, 45),
primary: Colors.green[800],
onPrimary: Colors.white,
),
onPressed: () async {
print('Email : $email');
print('passWord : $passWord');
if (await _connectivity.checkConnectivity() ==
ConnectivityResult.none) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text(
'Oups!',
style: TextStyle(fontSize: 22.0),
),
content: Text(
'Il faut te connecter à Internet !',
style: TextStyle(fontSize: 18.0),
),
actions: [
TextButton(
onPressed: () {
Navigator.pushReplacementNamed(
context, StartingScreen.id);
},
child: Text(
'OK',
style: TextStyle(fontSize: 18.0),
),
),
],
),
);
} else {
String accountVerif =
await uD.checkAccountId(email, passWord);
if (accountVerif == '') {
DialogBuilder(context).showLoadingIndicator(
text: 'Téléchargement des bases',
color: Colors.black45);
await uD.downLoadUserInfo(email);
DialogBuilder(context).hideOpenDialog();
Navigator.pushReplacementNamed(
context, ProfileScreen.id);
} else {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text(
'Oups!',
style: TextStyle(fontSize: 22.0),
),
content: Text(
'Ce compte n\'existe pas, ou le mot de passe est incorrect.',
style: TextStyle(fontSize: 18.0),
),
actions: [
TextButton(
onPressed: () {
Navigator.pushReplacementNamed(
context, StartingScreen.id);
},
child: Text(
'OK',
style: TextStyle(fontSize: 18.0),
),
),
],
),
);
}
}
}),
],
),
),
),
),
)),
],
),
);
return scaffold;
}
}
Please Try to define you password and email variables outside build method. It may solve issue.
See it works for me, May be you should do
stop execution
run 'flutter clean'
run 'flutter pub get'
execute it
Define the variables just below the override instead of below the build method
Add the textediting controller to it
so that your problem will be solved