从多个页面浏览量获取pageview的数据,并保存到firebase
我是Flutter Dart的新手,我的一个屏幕profilepage上有页面浏览量。页面外面的页面上有2个按钮,然后向后看,当用户到达最后一页时,下一个按钮更改为完成。
profilePage.dart
import 'dart:typed_data';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:stressed_app_admin/psychiatrist/widgets/contact-details.dart';
import 'package:stressed_app_admin/psychiatrist/widgets/document-upload.dart';
import 'package:stressed_app_admin/psychiatrist/widgets/educational-details.dart';
import 'package:stressed_app_admin/resources/auth_methods.dart';
import 'package:stressed_app_admin/resources/firestore_methods.dart';
import 'package:stressed_app_admin/resources/storage_methods.dart';
import '../utils/colors.dart';
import '../utils/global_varaibles.dart';
class ProfilePage extends StatefulWidget {
final String message;
const ProfilePage({Key? key, required this.message}) : super(key: key);
@override
ProfilePageState createState() => ProfilePageState();
}
class ProfilePageState extends State<ProfilePage> {
late PageController pageController;
final TextEditingController _username = TextEditingController();
final TextEditingController _email = TextEditingController();
final TextEditingController _firstName = TextEditingController();
final TextEditingController _lastName = TextEditingController();
final TextEditingController _contact = TextEditingController();
final TextEditingController _about = TextEditingController();
final TextEditingController _location = TextEditingController();
final TextEditingController _degree = TextEditingController();
final TextEditingController _dataPassOut = TextEditingController();
final TextEditingController _experience = TextEditingController();
final TextEditingController _university = TextEditingController();
Uint8List? _cvValue;
Uint8List? _degreeValue;
late final String cvUrl;
late final String degreeUrl;
late Color contactInfo;
late Color completedTask;
late Color suggestedTask;
int _page = 0;
var data;
bool _uploaded = true;
bool _isLoading = false;
bool _firstPage = true;
bool _lastPage = false;
@override
void initState() {
super.initState();
pageController = PageController();
getPsyData();
setState(() {
contactInfo = darkpurplish;
completedTask = secondaryColor;
suggestedTask = secondaryColor;
});
}
getPsyData() async {
setState(() {
_isLoading = true;
});
data = await AuthMethods().getCurrentPsyDetails();
setState(() {
_isLoading = false;
});
}
@override
void dispose() {
super.dispose();
pageController.dispose();
}
void navigationTapped(int page) {
pageController.animateToPage(
page,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
setState(() {
_page = page;
if (_page == 0) {
_firstPage = true;
_lastPage = false;
}
if (_page > 0 && _page < 2) {
_firstPage = false;
_lastPage = false;
}
if (_page == 2) {
_firstPage = false;
_lastPage = true;
}
});
if (page == 0) {
setState(() {
contactInfo = darkpurplish;
completedTask = secondaryColor;
suggestedTask = secondaryColor;
});
}
if (page == 1) {
setState(() {
contactInfo = secondaryColor;
completedTask = secondaryColor;
suggestedTask = darkpurplish;
});
}
if (page == 2) {
setState(() {
contactInfo = secondaryColor;
completedTask = blueColor;
suggestedTask = secondaryColor;
});
}
}
selectCv() async {
FilePickerResult? result = await FilePicker.platform
.pickFiles(type: FileType.custom, allowedExtensions: ['pdf']);
if (result != null) {
PlatformFile file = result.files.first;
setState(() {
_cvValue = file.bytes;
});
} else {
// User canceled the picker
}
}
selectDegree() async {
FilePickerResult? result = await FilePicker.platform
.pickFiles(type: FileType.custom, allowedExtensions: ['pdf']);
if (result != null) {
PlatformFile file = result.files.first;
setState(() {
_degreeValue = file.bytes;
});
} else {
// User canceled the picker
}
}
updateDocuments() async {
if (_cvValue != null && _degreeValue != null) {
cvUrl = await StorageMethods()
.uploadCvToStorage("PsychiatristProfilePics", _cvValue!);
degreeUrl = await StorageMethods()
.uploadDegreeToStorage("PsychiatristProfilePics", _degreeValue!);
if (_username.text.isNotEmpty &&
_firstName.text.isNotEmpty &&
_lastName.text.isNotEmpty &&
_contact.text.isNotEmpty &&
_location.text.isNotEmpty &&
_degree.text.isNotEmpty &&
_about.text.isNotEmpty &&
_dataPassOut.text.isNotEmpty &&
_experience.text.isNotEmpty &&
_university.text.isNotEmpty) {
Map<String, dynamic> psy = {
"username": _username.text,
"firstName": _firstName.text,
"lastName": _lastName.text,
"contact": _contact.text,
"location": _location.text,
"degree": _degree.text,
"about": _about.text,
"passOutDate": _dataPassOut.text,
"experience": _experience.text,
"university": _university.text,
"cvUrl": cvUrl,
"degreeUrl": degreeUrl,
"isVerified": "pending"
};
FireStoreMethods().updatePsychiatrist(psy);
displayDialog(context, "Successfully uploaded");
} else {
displayDialog(context, "Some fields missing data");
}
} else {
displayDialog(context, "not file selected");
}
}
@override
Widget build(BuildContext context) {
if (!_isLoading) {
List<Widget> profileItems = [
ContactDetails(
username: _username..text = data["username"],
email: _email..text = data["email"],
firstName: _firstName,
lastName: _lastName,
contact: _contact,
location: _location,
about: _about),
EducationalDetails(
degree: _degree,
dataPassOut: _dataPassOut,
experience: _experience,
university: _university),
DocumentUpload(cv: _cvValue, degree: _degreeValue),
];
return Scaffold(
backgroundColor: purplish,
body: Center(
child: Container(
width: MediaQuery.of(context).size.width * 0.55,
height: MediaQuery.of(context).size.height * 0.75,
decoration: BoxDecoration(
color: primaryColor,
boxShadow: [
BoxShadow(
color: Colors.black12.withOpacity(0.3),
spreadRadius: 10,
blurRadius: 6,
),
],
),
child: _uploaded && widget.message == "not-verified"
? Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
SizedBox(
height: 20,
),
Text(
"Complete the following Steps to get Selected as a Psychiatrist"
.toUpperCase(),
style: GoogleFonts.titilliumWeb(
fontSize: 22, color: darkpurplish),
),
SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InkWell(
onTap: () {
navigationTapped(0);
},
child: Container(
width: MediaQuery.of(context).size.width * 0.15,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Contact Details",
style: GoogleFonts.titilliumWeb(
fontSize: 16, color: contactInfo),
),
Container(
width: MediaQuery.of(context).size.width,
height: 1,
color: contactInfo,
)
],
),
),
),
SizedBox(
width: 2,
),
InkWell(
onTap: () {
navigationTapped(1);
},
child: Container(
width: MediaQuery.of(context).size.width * 0.15,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Educational Background",
style: GoogleFonts.titilliumWeb(
fontSize: 16, color: suggestedTask),
),
Container(
width: MediaQuery.of(context).size.width,
height: 1,
color: suggestedTask,
),
],
),
),
),
SizedBox(
width: 2,
),
InkWell(
onTap: () {
navigationTapped(2);
},
child: Container(
width: MediaQuery.of(context).size.width * 0.15,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Upload Documents",
style: GoogleFonts.titilliumWeb(
fontSize: 16, color: completedTask),
),
Container(
width: MediaQuery.of(context).size.width,
height: 1,
color: completedTask,
)
],
),
),
),
],
),
Expanded(
child: PageView(
children: profileItems,
physics: const NeverScrollableScrollPhysics(),
controller: pageController,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
_firstPage
? Container(
width: 94,
padding: EdgeInsets.symmetric(
horizontal: 11, vertical: 7),
color: secondaryColor,
child: Text(
"Back".toUpperCase(),
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 15, color: primaryColor),
),
)
: InkWell(
onTap: () {
navigationTapped(_page - 1);
},
child: Container(
width: 94,
padding: EdgeInsets.symmetric(
horizontal: 11, vertical: 7),
color: purplish,
child: Text(
"Back".toUpperCase(),
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 15, color: primaryColor),
),
),
),
SizedBox(
width: 15,
),
_lastPage
? InkWell(
onTap: () {
updateDocuments();
},
child: Container(
width: 94,
padding: EdgeInsets.symmetric(
horizontal: 11, vertical: 7),
color: purplish,
child: Text(
"Done".toUpperCase(),
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 15, color: primaryColor),
),
),
)
: InkWell(
onTap: () {
navigationTapped(_page + 1);
},
child: Container(
width: 94,
padding: EdgeInsets.symmetric(
horizontal: 11, vertical: 7),
color: purplish,
child: Text(
"Next".toUpperCase(),
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 15, color: primaryColor),
),
),
),
],
)
],
),
)
: Container(
padding: EdgeInsets.symmetric(horizontal: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Your request has been sent Successfully. You will informed Once it is Approved."
.toUpperCase(),
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 22, color: darkpurplish),
),
Text(
"For any issues or queries contact",
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 16, color: greyBlack),
),
Text(
"[email protected]",
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 15,
color: darkpurplish,
fontStyle: FontStyle.italic),
),
SizedBox(
height: 20,
),
InkWell(
child: Container(
width: 105,
padding: EdgeInsets.symmetric(
horizontal: 11, vertical: 7),
color: purplish,
child: Row(
children: [
Icon(
Icons.logout,
size: 15,
color: primaryColor,
),
SizedBox(
width: 5,
),
Text(
"logout".toUpperCase(),
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 15, color: primaryColor),
),
],
),
),
)
],
),
),
),
),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}
}
现在,当我填写数据时,我会按下一个:
- m第一个问题是如何确保用户填写所有字段,只有那时他/她才能移动下一页?
- 在我的最后一页上,当用户点击完成按钮时,我希望将所有3个页面视图中的所有数据上传到
我已经阅读了几乎所有有关stackoverflow和github上的pageviews的问题,但它们都不是根据我的要求或很容易理解的。如果您需要任何其他细节,我也可以分享。寻找一些简单的指南。谢谢。
I am new to flutter dart, I have pageview on one of my screen ProfilePage.dart having 3 pageveiws inside of it. There are 2 buttons on the page outside the page view NEXT and BACK and when the user gets to last page the next button changes to DONE.
Code for ProfilePage.dart
import 'dart:typed_data';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:stressed_app_admin/psychiatrist/widgets/contact-details.dart';
import 'package:stressed_app_admin/psychiatrist/widgets/document-upload.dart';
import 'package:stressed_app_admin/psychiatrist/widgets/educational-details.dart';
import 'package:stressed_app_admin/resources/auth_methods.dart';
import 'package:stressed_app_admin/resources/firestore_methods.dart';
import 'package:stressed_app_admin/resources/storage_methods.dart';
import '../utils/colors.dart';
import '../utils/global_varaibles.dart';
class ProfilePage extends StatefulWidget {
final String message;
const ProfilePage({Key? key, required this.message}) : super(key: key);
@override
ProfilePageState createState() => ProfilePageState();
}
class ProfilePageState extends State<ProfilePage> {
late PageController pageController;
final TextEditingController _username = TextEditingController();
final TextEditingController _email = TextEditingController();
final TextEditingController _firstName = TextEditingController();
final TextEditingController _lastName = TextEditingController();
final TextEditingController _contact = TextEditingController();
final TextEditingController _about = TextEditingController();
final TextEditingController _location = TextEditingController();
final TextEditingController _degree = TextEditingController();
final TextEditingController _dataPassOut = TextEditingController();
final TextEditingController _experience = TextEditingController();
final TextEditingController _university = TextEditingController();
Uint8List? _cvValue;
Uint8List? _degreeValue;
late final String cvUrl;
late final String degreeUrl;
late Color contactInfo;
late Color completedTask;
late Color suggestedTask;
int _page = 0;
var data;
bool _uploaded = true;
bool _isLoading = false;
bool _firstPage = true;
bool _lastPage = false;
@override
void initState() {
super.initState();
pageController = PageController();
getPsyData();
setState(() {
contactInfo = darkpurplish;
completedTask = secondaryColor;
suggestedTask = secondaryColor;
});
}
getPsyData() async {
setState(() {
_isLoading = true;
});
data = await AuthMethods().getCurrentPsyDetails();
setState(() {
_isLoading = false;
});
}
@override
void dispose() {
super.dispose();
pageController.dispose();
}
void navigationTapped(int page) {
pageController.animateToPage(
page,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
setState(() {
_page = page;
if (_page == 0) {
_firstPage = true;
_lastPage = false;
}
if (_page > 0 && _page < 2) {
_firstPage = false;
_lastPage = false;
}
if (_page == 2) {
_firstPage = false;
_lastPage = true;
}
});
if (page == 0) {
setState(() {
contactInfo = darkpurplish;
completedTask = secondaryColor;
suggestedTask = secondaryColor;
});
}
if (page == 1) {
setState(() {
contactInfo = secondaryColor;
completedTask = secondaryColor;
suggestedTask = darkpurplish;
});
}
if (page == 2) {
setState(() {
contactInfo = secondaryColor;
completedTask = blueColor;
suggestedTask = secondaryColor;
});
}
}
selectCv() async {
FilePickerResult? result = await FilePicker.platform
.pickFiles(type: FileType.custom, allowedExtensions: ['pdf']);
if (result != null) {
PlatformFile file = result.files.first;
setState(() {
_cvValue = file.bytes;
});
} else {
// User canceled the picker
}
}
selectDegree() async {
FilePickerResult? result = await FilePicker.platform
.pickFiles(type: FileType.custom, allowedExtensions: ['pdf']);
if (result != null) {
PlatformFile file = result.files.first;
setState(() {
_degreeValue = file.bytes;
});
} else {
// User canceled the picker
}
}
updateDocuments() async {
if (_cvValue != null && _degreeValue != null) {
cvUrl = await StorageMethods()
.uploadCvToStorage("PsychiatristProfilePics", _cvValue!);
degreeUrl = await StorageMethods()
.uploadDegreeToStorage("PsychiatristProfilePics", _degreeValue!);
if (_username.text.isNotEmpty &&
_firstName.text.isNotEmpty &&
_lastName.text.isNotEmpty &&
_contact.text.isNotEmpty &&
_location.text.isNotEmpty &&
_degree.text.isNotEmpty &&
_about.text.isNotEmpty &&
_dataPassOut.text.isNotEmpty &&
_experience.text.isNotEmpty &&
_university.text.isNotEmpty) {
Map<String, dynamic> psy = {
"username": _username.text,
"firstName": _firstName.text,
"lastName": _lastName.text,
"contact": _contact.text,
"location": _location.text,
"degree": _degree.text,
"about": _about.text,
"passOutDate": _dataPassOut.text,
"experience": _experience.text,
"university": _university.text,
"cvUrl": cvUrl,
"degreeUrl": degreeUrl,
"isVerified": "pending"
};
FireStoreMethods().updatePsychiatrist(psy);
displayDialog(context, "Successfully uploaded");
} else {
displayDialog(context, "Some fields missing data");
}
} else {
displayDialog(context, "not file selected");
}
}
@override
Widget build(BuildContext context) {
if (!_isLoading) {
List<Widget> profileItems = [
ContactDetails(
username: _username..text = data["username"],
email: _email..text = data["email"],
firstName: _firstName,
lastName: _lastName,
contact: _contact,
location: _location,
about: _about),
EducationalDetails(
degree: _degree,
dataPassOut: _dataPassOut,
experience: _experience,
university: _university),
DocumentUpload(cv: _cvValue, degree: _degreeValue),
];
return Scaffold(
backgroundColor: purplish,
body: Center(
child: Container(
width: MediaQuery.of(context).size.width * 0.55,
height: MediaQuery.of(context).size.height * 0.75,
decoration: BoxDecoration(
color: primaryColor,
boxShadow: [
BoxShadow(
color: Colors.black12.withOpacity(0.3),
spreadRadius: 10,
blurRadius: 6,
),
],
),
child: _uploaded && widget.message == "not-verified"
? Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
SizedBox(
height: 20,
),
Text(
"Complete the following Steps to get Selected as a Psychiatrist"
.toUpperCase(),
style: GoogleFonts.titilliumWeb(
fontSize: 22, color: darkpurplish),
),
SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InkWell(
onTap: () {
navigationTapped(0);
},
child: Container(
width: MediaQuery.of(context).size.width * 0.15,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Contact Details",
style: GoogleFonts.titilliumWeb(
fontSize: 16, color: contactInfo),
),
Container(
width: MediaQuery.of(context).size.width,
height: 1,
color: contactInfo,
)
],
),
),
),
SizedBox(
width: 2,
),
InkWell(
onTap: () {
navigationTapped(1);
},
child: Container(
width: MediaQuery.of(context).size.width * 0.15,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Educational Background",
style: GoogleFonts.titilliumWeb(
fontSize: 16, color: suggestedTask),
),
Container(
width: MediaQuery.of(context).size.width,
height: 1,
color: suggestedTask,
),
],
),
),
),
SizedBox(
width: 2,
),
InkWell(
onTap: () {
navigationTapped(2);
},
child: Container(
width: MediaQuery.of(context).size.width * 0.15,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Upload Documents",
style: GoogleFonts.titilliumWeb(
fontSize: 16, color: completedTask),
),
Container(
width: MediaQuery.of(context).size.width,
height: 1,
color: completedTask,
)
],
),
),
),
],
),
Expanded(
child: PageView(
children: profileItems,
physics: const NeverScrollableScrollPhysics(),
controller: pageController,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
_firstPage
? Container(
width: 94,
padding: EdgeInsets.symmetric(
horizontal: 11, vertical: 7),
color: secondaryColor,
child: Text(
"Back".toUpperCase(),
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 15, color: primaryColor),
),
)
: InkWell(
onTap: () {
navigationTapped(_page - 1);
},
child: Container(
width: 94,
padding: EdgeInsets.symmetric(
horizontal: 11, vertical: 7),
color: purplish,
child: Text(
"Back".toUpperCase(),
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 15, color: primaryColor),
),
),
),
SizedBox(
width: 15,
),
_lastPage
? InkWell(
onTap: () {
updateDocuments();
},
child: Container(
width: 94,
padding: EdgeInsets.symmetric(
horizontal: 11, vertical: 7),
color: purplish,
child: Text(
"Done".toUpperCase(),
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 15, color: primaryColor),
),
),
)
: InkWell(
onTap: () {
navigationTapped(_page + 1);
},
child: Container(
width: 94,
padding: EdgeInsets.symmetric(
horizontal: 11, vertical: 7),
color: purplish,
child: Text(
"Next".toUpperCase(),
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 15, color: primaryColor),
),
),
),
],
)
],
),
)
: Container(
padding: EdgeInsets.symmetric(horizontal: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Your request has been sent Successfully. You will informed Once it is Approved."
.toUpperCase(),
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 22, color: darkpurplish),
),
Text(
"For any issues or queries contact",
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 16, color: greyBlack),
),
Text(
"[email protected]",
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 15,
color: darkpurplish,
fontStyle: FontStyle.italic),
),
SizedBox(
height: 20,
),
InkWell(
child: Container(
width: 105,
padding: EdgeInsets.symmetric(
horizontal: 11, vertical: 7),
color: purplish,
child: Row(
children: [
Icon(
Icons.logout,
size: 15,
color: primaryColor,
),
SizedBox(
width: 5,
),
Text(
"logout".toUpperCase(),
textAlign: TextAlign.center,
style: GoogleFonts.titilliumWeb(
fontSize: 15, color: primaryColor),
),
],
),
),
)
],
),
),
),
),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}
}
Now when I fill in data I press next:
- M first question is how will I make sure that user has filled all fields and only then he/she can move next page ?
- On my Last Page when user Clicks on the DONE button I want all data from all 3 pageviews to be uploaded to my firebase database
I have read almost all the questions available to pageviews on stackoverflow as well as GitHub but none of them was either according to my requirements or quite easily understandable. If you need any further details I can share them too. Looking just for some simple hints of guide. THANK YOU.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我也是Flutter/Dart的新手,但是由于没有答案,我会尝试的。
在我看来,您的问题与处理数据相比,与其演示文稿有关,这是pageViews的主要业务。
对于1.,(我似乎看不到您的代码)是您的输入字段或多个表单小部件内部的输入字段,并且您是否查看了 https://docs.flutter.dev/cookbook/forms/forms/validation ?如果您对每个选项卡的内容都有一个状态表单小部件,我想您应该能够围绕
构建该逻辑,如果(_formkey.currentstate!.validate()){
我想,如果表单不是完全顺序,您将在一个包含的小部件中拥有其状态,当所有人都验证它们时,您应该是bale的2个。 flutter/upload-files” rel =“ nofollow noreferrer”> https://firebase.google.com/docs/storage/storage/flutter/upload-files 或一些类似。
希望会有所帮助 - 或更好的答案很快就会出现
I'm also new to flutter/dart but as there are no answers yet, I'll try.
It seems to me you questions have more to do with handling the data than with its presentation - which is the main business of the pageViews.
For 1., (I can't seem to see from your code) are your input fields inside of a or multiple form widgets, and have you looked at https://docs.flutter.dev/cookbook/forms/validation ? If you have a stateful form widget for each tab's content I imagine you should be able to build that logic around
if (_formKey.currentState!.validate()) {
I imagine, that if the forms are not entirely sequential, you would have their state in a containing widget, and when all of them validate you should be bale to take care of 2. as per https://firebase.google.com/docs/storage/flutter/upload-files or some such.
Hope that helps - or a better answer comes along soon