How to upload images in Firestore collection flutter - flutter

I have a user profil form , and i want the profile image to be stored in firestore with the other infromation of the user ! I did the part of stocking the information but idon't know how to store the image in the same document!
can you please help me ?
My code can just pick the picture , i didn't integrate the part of storing it in firestore
const kTextFieldDecoration = InputDecoration(
hintText: 'Enter a value',
hintStyle: TextStyle(color: Colors.grey),
contentPadding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xff5AA7A7), width: 1.0),
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xff5AA7A7), width: 2.0),
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
);
class Background extends StatefulWidget {
final Widget child;
const Background({
Key? key,
required this.child,
}) : super(key: key);
#override
_BackgroundState createState() => _BackgroundState();
}
class _BackgroundState extends State<Background>{
final _auth = FirebaseAuth.instance;
final _firebaseStorage = FirebaseStorage.instance ;
uploadImage() async {
Reference ref = _firebaseStorage.ref().child('profileimage');
UploadTask uploadTask = ref.putFile(imageFile);
var imageUrl = await ( await uploadTask).ref.getDownloadURL();
setState(() => {
imageurl= imageUrl.toString()
});
}
late File imageFile;
late String name;
late DateTime birth ;
late String pass ;
late String? genre ;
TextEditingController dateinput = TextEditingController();
final List<String> genderItems = [
'Male',
'Female',
];
String? selectedValue;
final ImagePicker _picker = ImagePicker();
bool showSpinner = false;
static final DateFormat format = DateFormat('yyyy-MM-dd');
File? imagePicked;
late Future<PickedFile?> pickedFile = Future.value(null);
String? imageurl ;
// Pick Image From Gallery
_getFromGallery() async {
final picker = ImagePicker();
final pickedImage = await picker.pickImage(
source: ImageSource.gallery,
);
final pickedImageFile = File(pickedImage!.path);
setState(() {
imagePicked = pickedImageFile;
});
}
void addPatient() {
FirebaseFirestore.instance.collection("patient").add(
{
"name" : name,
"genre" : genre,
"birth" : birth,
"pass" : pass,
}).then((value){
print("Patient data Added");
});
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
resizeToAvoidBottomInset: false ,
backgroundColor: Colors.white,
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
child : CircleAvatar(
radius: 70.0,
backgroundColor: Colors.grey,
backgroundImage: imagePicked == null
? null
: FileImage(
imagePicked!,
),
),
),
Container(
padding: EdgeInsets.only( left: 100.0),
child : Positioned(
bottom: 50.0,
right: 20.0,
child: InkWell(
onTap: () {
_getFromGallery();
},
child: Icon(
Icons.camera_alt_outlined,
size:28.0,color:
Colors.teal,
),
),
), ),
SizedBox(
height: 25.0,
),
SizedBox(
height: 8.0,
),
TextField(
keyboardType: TextInputType.text,
textAlign: TextAlign.center,
onChanged: (value) {
name = value;
},
decoration: kTextFieldDecoration.copyWith(
labelText: 'Name')),
SizedBox(
height: 8.0,
),
SizedBox(
height: 8.0,
),
TextField(
obscureText: true,
textAlign: TextAlign.center,
onChanged: (value) {
pass = value ;
},
decoration: kTextFieldDecoration.copyWith(
labelText: 'Password',)),
SizedBox(
height: 8.0,
),
SizedBox(
height: 8.0,
),
Column(
children: [
DropdownButtonFormField(
decoration:
//Add isDense true and zero Padding.
//Add Horizontal padding using buttonPadding and Vertical padding by increasing buttonHeight instead of add Padding here so that The whole TextField Button become clickable, and also the dropdown menu open under The whole TextField Button.
InputDecoration(
hintStyle: TextStyle(color: Colors.grey),
contentPadding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xff5AA7A7), width: 1.0),
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xff5AA7A7), width: 2.0),
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
//Add more decoration as you want here
//Add label If you want but add hint outside the decoration to be aligned in the button perfectly.
labelText: 'Gender',
),
isExpanded: true,
icon: const Icon(
Icons.arrow_drop_down,
color: Colors.teal,
),
iconSize: 30,
items: genderItems
.map((item) =>
DropdownMenuItem<String>(
value: item,
child: Text(
item,
style: const TextStyle(
fontSize: 14,
),
),
))
.toList(),
validator: (value) {
if (value == null) {
return 'Please select gender.';
}
},
onChanged: (value) {
genre = value as String? ; //Do something when changing the item if you want.
},
onSaved: (value) {
selectedValue = value.toString();
},
),
],
),
SizedBox(
height: 8.0,),
SizedBox(
height: 8.0,
),
TextField(
controller: dateinput,
textAlign: TextAlign.center,
readOnly: true,
decoration: InputDecoration(
hintStyle: TextStyle(color: Colors.grey),
contentPadding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xff5AA7A7), width: 1.0),
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xff5AA7A7), width: 2.0),
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
//icon of text field
labelText: "Date of Birth" , //label text of field
),
onTap: () => BottomPicker.date(
minDateTime: DateTime(1900),
maxDateTime: DateTime.now(),
dateOrder: DatePickerDateOrder.dmy ,
bottomPickerTheme: BOTTOM_PICKER_THEME.morningSalad,
closeIconColor: Colors.teal,
title: "Set your Birthday",
titleStyle: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
color: Colors.teal,
),
onSubmit: (value) {
dateinput.text = '$value';
birth = value ;
} ,
).show(context),
),
RoundedButton(
colour: Color(0xff96D7C6),
title: 'Submit',
onPressed: () async {
setState(() {
showSpinner = true;
addPatient();
});
Navigator.pushNamed(context, 'patient_screen');
}, ),],),),),),);}}

You can use Firebase Storage ( firebase_storage ) to upload the image file and then you store the download url of the image inside the document in the Cloud Firestore ( cloud_firestore ).
Here is the official docs for using Firebase Storage with Flutter, Inside you can find a section that explains the steps to upload files, and another to get the download URL.

Related

How to get data in a Flutter-ListView?

I'm building a form for shipping and am able to add as many items as possible. (Adding a Widget in a ListView every time a button is pressed)
My question is, once the form widgets are created and filled, how do I get the information from each TextFormField in each Widget?
Once the information is retrieved I will send it to Firebase.
Here's the code I Have:
import 'package:flutter/material.dart';
import 'package:flutter_login_test/helpers/constants.dart';
class AddItemsToRequest extends StatefulWidget {
const AddItemsToRequest({Key? key}) : super(key: key);
#override
State<AddItemsToRequest> createState() => _AddItemsToRequestState();
}
class _AddItemsToRequestState extends State<AddItemsToRequest> {
List<TextEditingController> controllers = [];
List<Widget> fields = [];
RegExp regExp = RegExp('[aA-zZ]');
int quantity = 0;
double weight = 0;
double height = 0;
Widget itemForm() {
return Column(children: [
Container(
decoration:
BoxDecoration(color: grey, border: Border.all(color: black)),
width: double.infinity,
child: const Center(
child: Text('Package details',
style: TextStyle(
fontWeight: FontWeight.bold,
backgroundColor: grey,
fontSize: 24)),
)),
Row(
children: [
Flexible(
child: TextFormField(
onChanged: (value) {
quantity = value as int;
},
keyboardType: TextInputType.number,
validator: (value) => value!.isEmpty || value is int
? 'Quantity cannot be empty'
: null,
autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: const InputDecoration(
errorStyle: TextStyle(color: Colors.redAccent),
border: OutlineInputBorder(
borderSide: BorderSide(),
borderRadius: BorderRadius.all(
Radius.circular(0.0),
),
),
fillColor: Color.fromARGB(255, 238, 238, 238),
filled: true,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blueAccent, width: 2.5),
),
hintText: "Quantity : "),
),
),
Flexible(
child: TextFormField(
onChanged: (value) {
weight = value as double;
},
keyboardType: TextInputType.number,
validator: (value) => value!.isEmpty || regExp.hasMatch(value)
? 'Weight cannot be empty'
: null,
autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: const InputDecoration(
errorStyle: TextStyle(color: Colors.redAccent),
border: OutlineInputBorder(
borderSide: BorderSide(),
borderRadius: BorderRadius.all(
Radius.circular(0.0),
),
),
fillColor: Color.fromARGB(255, 238, 238, 238),
filled: true,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blueAccent, width: 2.5),
),
hintText: "Weight : "),
),
),
Flexible(
child: TextFormField(
onChanged: (value) {
height = value;
},
validator: (value) => value!.isEmpty ? 'Height cannot be empty' : null,
autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: const InputDecoration(
errorStyle: TextStyle(color: Colors.redAccent),
border: OutlineInputBorder(
borderSide: BorderSide(),
borderRadius: BorderRadius.all(
Radius.circular(0.0),
),
),
fillColor: Color.fromARGB(255, 238, 238, 238),
filled: true,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blueAccent, width: 2.5),
),
hintText: "Height : "),
),
),
],
),
]);
}
Widget _addTile() {
return ElevatedButton(
child: const Icon(Icons.add),
onPressed: () async {
final controller = TextEditingController();
final field = itemForm();
setState(() {
controllers.add(controller);
fields.add(field);
});
});
}
Widget _listView() {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: fields.length,
itemBuilder: (context, index) {
final item = fields[index];
return Dismissible(
key: ObjectKey(item),
onDismissed: (direction) {
// Remove the item from the data source.
setState(() {
fields.removeAt(index);
});
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(content: Text('Package removed')));
},
background: Container(
color: const Color.fromARGB(255, 210, 31, 19),
child: const Center(
child: Text(
'Remove ',
style: TextStyle(
color: white, fontWeight: FontWeight.bold, fontSize: 32),
),
)),
child: Container(
width: double.infinity,
margin: const EdgeInsets.all(5),
child: fields[index],
),
);
},
);
}
Widget _okButton() {
return ElevatedButton(
onPressed: () {
for (var element in fields) {
print(quantity);
}
Navigator.of(context).pop();
print('ok');
},
child: const Text("OK"),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(backgroundColor: blue),
body: Column(
mainAxisSize: MainAxisSize.max,
children: [
Flexible(child: _addTile()),
SizedBox(
height: MediaQuery.of(context).size.height - 200,
child: _listView()),
Flexible(child: _okButton()),
],
),
);
}
}
I think you are expecting this answer. iterate your list of texteditingcontroller and get the text stored in that controllers one by one.
for (int i = 0; i < controllers.length; i++) {
var data = controllers[i].text;
//print or add data to any other list and insert to another list and save to database
}

how can I make scrollable and responsive screen flutter

I have implemented a login screen using flutter. the screen is fully functional and working properly with validations. I want this screen to be scrollable and responsive to other devices. how can I do that? below I have added my full login screen code. appreciate your help on this.
how to scroll the screen
how to implement responsive screen
class LoginScreen extends StatefulWidget {
const LoginScreen({Key? key}) : super(key: key);
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
#override
Widget build(BuildContext context) {
final Size = MediaQuery.of(context).size;
return GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color.fromARGB(255, 3, 86, 124), Color(0xff141a3a)],
begin: Alignment.topRight,
end: Alignment.bottomLeft,
)),
child: Scaffold(
backgroundColor: Colors.transparent,
resizeToAvoidBottomInset: false,
body: Padding(
padding: const EdgeInsets.only(top: 40, left: 20, right: 20),
child: Column(
children: [
Expanded(
flex: 2,
child: Column(
children: [
// Spacer(flex: 1),
Image(
image: const AssetImage(
'assets/images/LogoVector.png',
),
height: Size.width / 2.9,
width: Size.width / 2.9,
),
SizedBox(height: 5),
Text(
"LOGIN",
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 30,
color: textWhite,
fontFamily: "Roboto"),
),
],
),
),
// const Spacer(flex: 1),
const Expanded(flex: 3, child: Center(child: LoginForm())),
// Spacer(
// flex: 1,
// ),
],
),
),
),
),
);
}
}
class LoginForm extends StatefulWidget {
const LoginForm({Key? key}) : super(key: key);
#override
_LoginFormState createState() => _LoginFormState();
}
Map<String, String> loginUserData = {
'email': '',
'password': '',
'id': '',
'userName': '',
'token': '',
'userStatus': '',
};
class _LoginFormState extends State<LoginForm> {
TextEditingController emailEditingController = new TextEditingController();
TextEditingController passwordEditingController = new TextEditingController();
final _formKey = GlobalKey<FormState>();
String email = "";
String password = "";
String username = "";
bool isLoading = false;
bool typing = true;
bool _isObscure = true;
// bool newValue = true;
bool checkedValue = true;
//String fcmToken = '';
Future LoginData() async {
setState(() {
isLoading = true;
typing = false;
});
try {
var response = await Dio().post(BASE_API + 'user/login',
data: {"username": email, "password": password});
if (response.data["status"] == "LoginSuccess") {
setState(() {
isLoading = false;
});
Get.snackbar(
"success",
"logged in successfully",
backgroundColor: buttontext.withOpacity(0.5),
colorText: textWhite,
borderWidth: 1,
borderColor: Colors.grey,
);
Get.to(BottomNavigation());
} else {
setState(() {
isLoading = false;
typing = true;
});
Get.snackbar(
"error",
"No User Found",
backgroundColor: buttontext.withOpacity(0.5),
colorText: textWhite,
borderWidth: 1,
borderColor: Colors.grey,
);
}
print("res: $response");
setState(() {
isLoading = false;
typing = true;
});
} catch (e) {
setState(() {
isLoading = false;
typing = true;
});
Get.snackbar("Error", "Something went wrong.Please contact admin",
backgroundColor: buttontext.withOpacity(0.5),
borderWidth: 1,
borderColor: Colors.grey,
colorText: Colors.white,
icon: Icon(
Icons.error_outline_outlined,
color: Colors.red,
size: 30,
));
print(e);
}
}
#override
Widget build(BuildContext context) {
final Size = MediaQuery.of(context).size;
return Form(
key: _formKey,
autovalidateMode: AutovalidateMode.disabled,
child: Column(
children: [
TextFormField(
controller: emailEditingController,
enabled: true,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(
color: textWhite,
),
// borderSide: BorderSide.none
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(color: textWhite),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(color: Colors.red),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(color: Colors.red),
),
isDense: true,
contentPadding: EdgeInsets.fromLTRB(10, 30, 10, 0),
hintText: "Email/ Username",
hintStyle: TextStyle(
color: textWhite, fontFamily: "Roboto", fontSize: 14),
),
style: TextStyle(color: textWhite),
validator: (String? UserName) {
if (UserName != null && UserName.isEmpty) {
return "Email can't be empty";
}
return null;
},
onChanged: (String? text) {
email = text!;
// print(email);
},
onSaved: (value) {
loginUserData['email'] = value!;
},
),
SizedBox(
height: 10,
),
TextFormField(
controller: passwordEditingController,
obscureText: _isObscure,
enabled: true,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(color: textWhite),
// borderSide: BorderSide.none
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(color: textWhite),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.red),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.red),
),
isDense: true,
contentPadding: EdgeInsets.fromLTRB(10, 10, 10, 0),
suffixIcon: IconButton(
icon: Icon(
_isObscure ? Icons.visibility : Icons.visibility_off),
color: textWhite,
onPressed: () {
setState(() {
_isObscure = !_isObscure;
});
}),
hintText: "Password",
hintStyle: TextStyle(
color: textWhite,
fontFamily: "Roboto",
fontSize: 14,
)),
style: TextStyle(color: textWhite),
validator: (String? Password) {
if (Password != null && Password.isEmpty) {
return "Password can't be empty";
}
return null;
},
onChanged: (String? text) {
password = text!;
print(password);
},
onSaved: (value) {
loginUserData['password'] = value!;
},
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: CheckboxListTile(
title: const Text(
"Remember Me",
style: TextStyle(
color: textWhite, fontFamily: "Roboto", fontSize: 14),
),
activeColor: buttontext,
// tileColor: buttontext,
value: checkedValue,
onChanged: (newValue) {
FocusManager.instance.primaryFocus?.unfocus();
setState(() {
if (isLoading != true) {
checkedValue = newValue!;
print(newValue);
}
});
},
contentPadding: EdgeInsets.only(left: 0, top: 0),
controlAffinity:
ListTileControlAffinity.leading, // <-- leading Checkbox
),
),
TextButton(
child: Text(
"Forget Password",
style: TextStyle(
color: textWhite, fontFamily: "Roboto", fontSize: 14),
),
onPressed: () {
Get.to(() => Forget_Screen());
},
)
],
),
SizedBox(height: 40),
isLoading
? SpinKitDualRing(
color: textWhite,
size: 40,
)
: GestureDetector(
child: MainButton("Login"),
onTap: () async {
FocusManager.instance.primaryFocus?.unfocus();
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
await LoginData();
// Get.to(BottomNavigation());
}
},
),
SizedBox(height: 15),
Container(
width: 275.0,
height: 40.0,
child: OutlinedButton(
onPressed: () {
Get.to(() => const Signup_Screen());
},
child: const Text(
'Signup',
style: TextStyle(
fontSize: 15, fontFamily: "Roboto", color: textWhite
//fontWeight: FontWeight.w500,
// color: Colors.black,
),
),
style: OutlinedButton.styleFrom(
side: const BorderSide(
width: 1.0,
color: textWhite,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
// side: BorderSide(width: 2, color: Colors.green),
),
),
),
)
],
),
);
}
}
How to scroll the screen? Use SingleChildScrollView wrap your Column, remember to ristrict the content size to avoid error.
How to implement responsive screen? Simplest is use LayoutBuilder widget that provide constrains as a props. This constrains can provide maxHeight, maxWidth,... You can then rely on it to determine how you build your application (responsive).
Example:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: LayoutBuilder(
builder: (context, constrains){
final maxw = constrains.maxWidth;
String text = '';
if(maxw < 420) {text = 'Smaller than 420';}
else {text = 'Bigger than 420';}
return Center(child: Text('Width is: $maxw, $text'));
}
),
);
}

How to check time is after 3 min from a time or not in Flutter | Dart

I want to edit a form only after 3 minutes from the last edit. I am getting a last update time as string from api when loading that form for eg: 11:53:09. So i want to show submit form button only after 3 minutes from the last update time. Otherwise i want to show a Text that form can be edited after 3 minutes. How to acheive this functionality.
Hello there you can achieve this by using simple logic, here is the basic code for this implementation:
import 'package:flutter/material.dart';
class LoginScreen extends StatefulWidget {
static const String id = 'login_screen';
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final formKey = GlobalKey<FormState>();
bool _canSubmit = true;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Form(
key: formKey,
child: Stack(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(30.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
TextFormField(
onChanged: (value) {},
obscureText: false,
style: TextStyle(
color: Colors.black54,
fontSize: 16.0,
fontWeight: FontWeight.w700),
decoration: InputDecoration(
labelStyle: TextStyle(color: Colors.black54),
focusColor: Colors.black54,
filled: true,
enabledBorder: UnderlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.black54),
),
labelText: "Email"),
validator: (value) {
if (value.isEmpty ||
!RegExp(r'^[\w-\.]+#([\w-]+\.)+[\w-]{2,4}$')
.hasMatch(value)) {
return "Enter Correct Email Address";
} else {
return null;
}
},
),
SizedBox(
height: 10.0,
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
TextFormField(
onChanged: (value) {},
style: TextStyle(
color: Colors.black54,
fontSize: 16.0,
fontWeight: FontWeight.w700),
decoration: InputDecoration(
labelStyle: TextStyle(color: Colors.black54),
focusColor: Colors.black54,
filled: true,
enabledBorder: UnderlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.black54),
),
labelText: "Password"),
validator: (value) {
if (value.isEmpty) {
return "Enter Valid Password";
} else {
return null;
}
},
)
],
),
SizedBox(
height: 10.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
constraints:
BoxConstraints(maxHeight: 36, maxWidth: 36),
icon: Icon(
Icons.lock,
size: 20,
color: Colors.black54,
),
onPressed: _canSubmit
? () {
if (formKey.currentState.validate()) {
FocusManager.instance.primaryFocus
?.unfocus();
submitPressed();
}
}
: null)
]),
],
),
),
],
),
));
}
Future<void> submitPressed() async {
setState(() {
_canSubmit = false;
});
Future.delayed(Duration(minutes: 3), () {
setState(() {
_canSubmit = true;
});
});
}
}
You can parse the last update time to DateTime and compare it with DateTime.now()
updatedTime = 11:53:09
DateFormat format = new DateFormat("hh:mm:ss");
updatedTime = format.format(updatedTime);
in your widget add this
DateTime.now() - updatedTime < 3 ? Text("form can be edited after 3 minutes") : Form()
you can parse you datetime in DateTime.parse() and then you can use it like this
DateTime.now().isAfter(lastEditedDateTime.add(const Duration(minutes: 3)));
in addition to this you can also use future delay to update you button enable variable to update every second.

How to clear the dropdown button in flutter

im new in flutter and I create a drop down button fields in flutter for the record form. My question is when I click save in the record, how can I make the dropdown is clear (back to the first or no value) for now when I click save, it did not reset the drop down value (still have a value from previous record) And also when I click the clear button, how can I make the drop down is clear? Thanks
class RecordExpense extends StatefulWidget {
#override
_RecordExpenseState createState() => _RecordExpenseState();
}
class _RecordExpenseState extends State<RecordExpense> {
//DatabaseReference _ref;
final date = TextEditingController();
final currency = TextEditingController();
final category = TextEditingController();
final amount = TextEditingController();
final description = TextEditingController();
final FirebaseAuth _auth = FirebaseAuth.instance;
final databaseReference = FirebaseFirestore.instance;
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
String _email, _password;
Future<String> getCurrentUID() async {
Future.value(FirebaseAuth.instance.currentUser);
//return uid;
}
#override
String selectCurrency;
String selectExpense;
final expenseSelected = TextEditingController();
final currencySelected = TextEditingController();
DateTime _selectedDate;
void initState(){
//_ref = FirebaseDatabase.instance.reference().child('Transaction');
}
Widget build(BuildContext context) {
//FirebaseFirestore firestore = FirebaseFirestore.instance;
//CollectionReference collect= firestore.collection("TransactionExpense");
final FirebaseAuth _auth = FirebaseAuth.instance;
final User user =_auth.currentUser;
final uid = user.uid;
String dates;
String amounts;
//String selectExpenses;
String descriptions;
return new Form(
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(20.0),
child: Container(
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
child: TextFormField(
validator: (input) {
if (input.isEmpty) return 'Please fill up the text fields';
},
cursorColor: Colors.grey,
controller: date,
onTap: () {
_selectDate(context);
},
decoration: InputDecoration(
labelText: getTranslated((context), "date_text"),
labelStyle: TextStyle(
fontSize: 18.0, color: Colors.black),
hintText: getTranslated((context), "date_hint"),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: secondary),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: secondary),
),
),
),
),
SizedBox(height: 20),
Row(
children: <Widget> [
new Expanded(child: new DropdownButtonFormField<String>(
value: selectCurrency,
hint: Text(getTranslated((context), "currency_hint"),),
//controller: currencySelected,
//labelText: getTranslated((context), "currency_hint"),
//enabled: true,
//itemsVisibleInDropdown: 4,
//items: currencycategories,
onChanged: (salutation) =>
setState(() => selectCurrency = salutation),
validator: (value) => value == null ? 'Please fill up the drop down' : null,
items:
['IDR.', 'MYR', 'USD', 'CNY'].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
//flex: 2,
),
//value: selectCurrency,
//required: false,
),
new SizedBox(
width: 10.0,
),
new Expanded(child:
TextFormField(
validator: (input) {
if (input.isEmpty) return 'Please fill up the text fields';
},
cursorColor: Colors.grey,
controller: amount,
decoration: InputDecoration(
labelText: getTranslated((context), "amount_text"),
labelStyle: TextStyle(
fontSize: 18.0, color: Colors.black),
hintText: getTranslated((context), "amount_text"),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: secondary),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: secondary),
),
),
keyboardType: TextInputType.number,
),)
],
),
Container(
padding: EdgeInsets.only(top: 20.0),
child: DropdownButtonFormField <String>(
value: selectExpense,
hint: Text(getTranslated((context), "category_text"),),
//controller: currencySelected,
//labelText: getTranslated((context), "currency_hint"),
//enabled: true,
//itemsVisibleInDropdown: 4,
//items: currencycategories,
onChanged: (salutation) =>
setState(() => selectExpense = salutation),
validator: (value) => value == null ? 'Please fill up the drop down' : null,
items:
['Food.', 'Social Life', 'Transportation', 'Beauty', 'Household', 'Education', 'Health', 'Gift', 'Other'].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
//flex: 2,
),
),
SizedBox(height: 20),
Container(
//padding: EdgeInsets.all(20),
child: TextFormField(
validator: (input) {
if (input.isEmpty) return 'Please fill up the text fields';
},
cursorColor: Colors.grey,
controller: description,
maxLines: 2,
decoration: InputDecoration(
labelText: getTranslated((context), "description_text"),
labelStyle: TextStyle(
fontSize: 18.0, color: Colors.black),
hintText: getTranslated((context), "description_expense"),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: secondary),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: secondary),
),
),
),
),
Container(
padding: EdgeInsets.only(
top: 25.0, left: 20.0, right: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: ElevatedButton(
onPressed: () async {
if(!_formKey.currentState.validate()){
return;
}
_formKey.currentState.save();
await FirebaseFirestore.instance.collection('users').doc(userID).collection('TransactionExpense').add({
'date': date.text,
'currency': selectCurrency,
'amount': amount.text,
'category': selectExpense,
'description': description.text,
});
date.text = "";
amount.text = "";
description.text = "";
//selectCurrency = "";
//selectExpense = "";
/*
UserCredential _user =
await FirebaseAuth.instance.signInWithEmailAndPassword(email: _email, password: _password);
String _uid = _user.user.uid;
*/
//await FirebaseFirestore.instance.collection('TransactionExpense').doc(_uid).set({
/*
final FirebaseAuth _auth = FirebaseAuth
.instance;
final User user = _auth.currentUser;
final uid = user.uid;
await DatabaseService().updateData(
uid, date.text, amount.text,
selectExpense, description.text);
Navigator.pop(context);
*/
},
child: Text(
getTranslated((context), "save_button").toUpperCase(), style: TextStyle(
fontSize: 14,
)),
style: ButtonStyle(
padding: MaterialStateProperty.all<
EdgeInsets>(EdgeInsets.all(15)),
foregroundColor: MaterialStateProperty
.all<Color>(Colors.white),
backgroundColor: MaterialStateProperty
.all<Color>(Colors.pink),
shape: MaterialStateProperty.all<
RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
15.0),
side: BorderSide(color: secondary)
),
),
),
),
),
SizedBox(width: 20, height: 10),
Expanded(
child: ElevatedButton(
onPressed: () {
clearButton();
},
child: Text(
getTranslated((context), "clear_button").toUpperCase(), style: TextStyle(
fontSize: 14
)),
style: ButtonStyle(
padding: MaterialStateProperty.all<
EdgeInsets>(EdgeInsets.all(15)),
foregroundColor: MaterialStateProperty
.all<Color>(Colors.white),
backgroundColor: MaterialStateProperty
.all<Color>(Colors.pink),
shape: MaterialStateProperty.all<
RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
15.0),
side: BorderSide(color: secondary)
),
),
),
),
)
],
)
),
],
),
),
),
)
),
);
}
void clearButton(){
date.clear();
amount.clear();
category.clear();
description.clear();
//selectCurrency.clear();
}
_selectDate(BuildContext context) async {
DateTime newSelectedDate = await showDatePicker(
context: context,
initialDate: _selectedDate != null ? _selectedDate : DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2040),
builder: (BuildContext context, Widget child) {
return Theme(
data: ThemeData.dark().copyWith(
colorScheme: ColorScheme.dark(
primary: secondary,
onPrimary: Colors.black,
surface: primary,
onSurface: Colors.white,
),
dialogBackgroundColor: Colors.black,
),
child: child,
);
});
if (newSelectedDate != null) {
_selectedDate = newSelectedDate;
date
..text = DateFormat.yMMMd().format(_selectedDate)
..selection = TextSelection.fromPosition(TextPosition(
offset: date.text.length,
affinity: TextAffinity.upstream));
}
}
}
class AlwaysDisabledFocusNode extends FocusNode {
#override
bool get hasFocus => false;
}
When the user taps on the save button the onPressed function is executed, inside that function use setstate to set selectCurrency = null; & selectExpense = null;. This will make the dropdownbutton show the hint instead of old record

How to retrieve value from multiple text field created with one method in flutter?

i have created multiple text field using a single method in different file how i retrieve the value from them. I want to retrieve the value in different variables.
//class method
class CustomTextField {
static TextField display(BuildContext context, [String name, double size=16,IconData icon]) {
return new TextField(
textAlign: TextAlign.center,
style: CustomTextStyle.display(context, Colors.black38, size),
decoration: new InputDecoration(
alignLabelWithHint: true,
icon: new Icon(icon),
contentPadding:EdgeInsets.only(top: 30,right: 30,),
border: InputBorder.none,
hintText:"$name",
focusedBorder: InputBorder.none,
hintStyle: CustomTextStyle.display(context, Colors.grey, size),
),
);
}
}
//call method
new Container(
width: width,
height: height,
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(Radius.circular(20)),
gradient: new LinearGradient(
colors: [
Color.fromRGBO(252, 191, 93, 1),
Color.fromRGBO(255, 210, 119, 1),
Color.fromRGBO(252, 215, 85, 1),
],
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
),
),
child: CustomTextField.display(context, name,16,icon),
),
First of all, I recommend that you use Stateless Widget to create your TextField instead of using a static function like this code below:
class CustomTextField extends StatelessWidget {
final Function(String) onChanged;
final String name;
final double size;
final IconData icon;
CustomTextField({
this.onChanged,
this.name,
this.size: 16,
this.icon,
});
Widget build(BuildContext context) {
return new TextField(
textAlign: TextAlign.center,
onChanged: onChanged,
style: CustomTextStyle.display(context, Colors.black38, size),
decoration: new InputDecoration(
alignLabelWithHint: true,
icon: new Icon(icon),
contentPadding: EdgeInsets.only(
top: 30,
right: 30,
),
border: InputBorder.none,
hintText: "$name",
focusedBorder: InputBorder.none,
hintStyle: CustomTextStyle.display(context, Colors.grey, size),
),
);
}
}
Then, create a function for the onChange field to receive the new value:
class App extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: width,
height: height,
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(Radius.circular(20)),
gradient: new LinearGradient(
colors: [
Color.fromRGBO(252, 191, 93, 1),
Color.fromRGBO(255, 210, 119, 1),
Color.fromRGBO(252, 215, 85, 1),
],
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
),
),
child: CustomTextField(
// Here you get the value change
onChanged: (value) {
print('Text value changed');
print('New value: $value');
},
name: name,
size: 16,
icon: icon,
),
),
);
}
}
Use Function as a parameter for your CustomTextField and add it to onChanged parameter of TextField;
class CustomTextField {
static TextField display(BuildContext context, Function onChanged,
[String name, double size = 16, IconData icon]) {
return new TextField(
textAlign: TextAlign.center,
onChanged: onChanged,
style: CustomTextStyle.display(context, Colors.black38, size),
decoration: new InputDecoration(
alignLabelWithHint: true,
icon: new Icon(icon),
contentPadding: EdgeInsets.only(
top: 30,
right: 30,
),
border: InputBorder.none,
hintText: "$name",
focusedBorder: InputBorder.none,
hintStyle: CustomTextStyle.display(context, Colors.grey, size),
),
);
}
}
then get your value;
CustomTextField.display(
context,
(value) {
print(value);
},
name,
16,
icon,
),
you should provide a function to handle the onChange property of your custom text field.
here is how i used one in a project of mine:
the custom class widget
class MyTextField extends StatefulWidget {
final String title;
final Function onChange; // you can get the value from this function
final bool isPassword;
MyTextField(this.title, this.onChange, {this.isPassword = false});
#override
_MyTextFieldState createState() => _MyTextFieldState();
}
class _MyTextFieldState extends State<MyTextField> {
bool showPassword = false;
final _controller = TextEditingController();
#override
Widget build(BuildContext context) {
_controller.addListener(() {
setState(() {});
});
return Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
widget.title,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
SizedBox(height: 2),
TextField(
controller: _controller,
onChanged: widget.onChange,
obscureText: !showPassword && widget.isPassword,
decoration: InputDecoration(
suffixIcon: widget.isPassword
? IconButton(
icon: Icon(
Icons.remove_red_eye,
color: showPassword ? Colors.blue : Colors.grey,
),
onPressed: () {
setState(() => showPassword = !showPassword);
},
)
: IconButton(
icon: Icon(
Icons.clear,
color: _controller.text.isEmpty
? Colors.grey
: Colors.blue,
),
onPressed: () => _controller.clear()),
border: InputBorder.none,
fillColor: Color(0xfff3f3f4),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue, width: 5.0),
),
filled: true),
)
],
),
);
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
}
the use case:
MyTextField('Email', (value) => email = value.trim()), // body of onChange
MyTextField(
'Password',
(value) => password = value.trim(), // body of onChange
isPassword: true,
),
// value is what you get from the text fields.
Provide a TextEditingController as a parameter to construct your CustomTextField.