How to generate and validate a list of TextEditingController and upload the List of textEditingController to firebase -flutter - firebase

I created an upload page where user picks multiple images and then assign a price for each image.I'm want to achieve this with a list of TextEditing controllers and displaying textfield under each image.
First I created the list and later generate the list according to the length of the images.
List<TextEditingController> _controller;
Widget buildGridView() {
return Container(
height: 400,
child: GridView.count(
crossAxisCount: 1,
scrollDirection: Axis.horizontal,
children: List.generate(images.length, (index) {
//I can't use the index I defined below in controller.
//says (i) is undefined so I used the index which defined above.
//Am I doing it right?
_controller = List.generate(images.length, (i) => TextEditingController());
Asset asset = images[index];
return Padding(
padding: EdgeInsets.all(8.0),
child: Container(
height: 100,
width: 100,
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15)),
child: AssetThumb(
asset: asset,
width: 300,
height: 300,
),
),
Form(
key: _formKey,
child: TextFormField(
keyboardType: TextInputType.number,
validator: (String value) {
double sal = double.tryParse(value);
if (sal == null) {
return 'enter or delete row';
}
return null;
},
controller: _controller[index],
),
),
],
),
),
);
}),
),
);
}
Here _formkey gives me error duplicate key found.Can I generate a list of formKeys according to the length of images?
at last I want to upload the list of textEditingControllers. I has .toList() parameter but not .text

You should have just one Form widget with one FormKey and that should wrap the all the TextFormField widgets.
Form class
An optional container for grouping together multiple form field
widgets (e.g. TextField widgets).
https://api.flutter.dev/flutter/widgets/Form-class.html
So in this case, your Form should wrap the GridView.
Here is your buildGridView method with the update:
Widget buildGridView() {
return Container(
height: 400,
child: Form(
key: _formKey,
child: GridView.count(
crossAxisCount: 1,
scrollDirection: Axis.horizontal,
children: List.generate(images.length, (index) {
//I can use the index I defined below in controller.
//says i is undefined so I used the index which defined above.
//Am I doing it right?
_controller =
List.generate(images.length, (i) => TextEditingController());
Asset asset = images[index];
return Padding(
padding: EdgeInsets.all(8.0),
child: Container(
height: 100,
width: 100,
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15)),
child: AssetThumb(
asset: asset,
width: 300,
height: 300,
),
),
TextFormField(
keyboardType: TextInputType.number,
validator: (String value) {
double sal = double.tryParse(value);
if (sal == null) {
return 'enter or delete row';
}
return null;
},
controller: _controller[index],
),
],
),
),
);
}),
),
),
);
}
In order to get the list of text from the controller list, you need to map each controller to get the text.
List<String> textList = _controller.map((x) => x.text).toList();

Related

(Flutter) How to retrieve Firebase Storage images in a Streambuilder with the paths stored in Firestore

In my code, I have a streambuilder that returns items from Firestore using the geoflutterfire package to load items that are within a radius of the user.
These items are loaded, along with the food title, allergies, and age which are retrieved from Firestore. Along with the mentioned fields, there is also an imagePath field.
The image path is a subItem in the test folder in Firebase Storage:
Right now, there is a placeholder image, the image of the MacBook, over the spot where I want my image to be. Since the query loads the image path along with the name, allergies, etc, is it possible to show an image given its image path inside of a streambuilder? Here is the code for the query and streambuilder.
Query:
var collectionReference = FirebaseFirestore.instance.collection('requests');
double radius = 1000;
String field = 'position';
Stream<List<DocumentSnapshot>> stream = geo.collection(collectionRef: collectionReference)
.within(center: center, radius: radius, field: field);
Streambuilder: (Scroll to //CODE OF PLACEHOLDER IMAGE to find placeholder image)
StreamBuilder(
stream: stream,
builder: (BuildContext context,
AsyncSnapshot<List<DocumentSnapshot>> snapshots) {
if (snapshots.connectionState == ConnectionState.active &&
snapshots.hasData) {
return ListView(
physics: const BouncingScrollPhysics(),
children: snapshots.data!.map(
(DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
color: const Color(0xff303434),
),
child: Row(
children: [
//CODE OF PLACEHOLDER IMAGE
Padding(
padding: const EdgeInsets.all(15.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(40.0),
child: Image.network(
'https://picsum.photos/250?image=9',
fit: BoxFit.fill,
width: 150,
height: 150,
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 140,
),
child: AutoSizeText(
data['foodCategory'].toString(),
maxLines: 1,
style: GoogleFonts.poppins(
color: Colors.white,
fontSize: 25,
fontWeight: FontWeight.w900,
),
),
),
const SizedBox(
height: 5,
),
ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 70.0,
maxWidth: 140,
),
child: AutoSizeText(
data['allergens'],
minFontSize: 10,
maxFontSize: 25,
style: GoogleFonts.poppins(
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
),
const SizedBox(
height: 5,
),
AutoSizeText(
data['ageNum'].toString() +
' ' +
data['ageUnit'].toString(),
maxLines: 1,
style: GoogleFonts.poppins(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.w400,
),
),
],
),
],
),
);
},
).toList(),
);
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
Your image url are wrong.
You should get image url after upload image to Firestore
Example below code:
var file = _imageFile;
FirebaseStorage firebaseStorage = FirebaseStorage.instance;
Reference ref = firebaseStorage.ref(
'uploads-images/${user.userId}/images/${DateTime.now().microsecondsSinceEpoch}');
TaskSnapshot uploadedFile = await ref.putFile(file);
if (uploadedFile.state == TaskState.success) {
downloadUrl = await ref.getDownloadURL();
}
And iamgePath is downloadUrl
Check your rule on Firestore if you don't want to authenticate Firestore

Is there a way to compare two list and check for the value that are the same

I am create a social media type app I want to create a follower and following list like Instagram, I want when I go on some else profile and click on their followers or following list it shows a list of all the users that is following that person and if I am also following someone in the list it give me the option to unfollow that person and if I am not following the same person it gives me the option to follow them.
The code below is what I was to check if I am following any one in profile user followers list, What I have done is I query the data from firestore of all the user id the I am following and the same for user profile that is currently open and store the list in two separate variable and what it should do is check both list and if a user Id is in both list it means I am also following that user and should show that I am following that user and give the option to unfollow but what happens is instead of showing that I am only following the user who's id is in both list it show that I am following every one.
checkfollowers(BuildContext context, String? id) async {
final auth = Provider.of<AuthState>(context, listen: false);
List<String> followingList = [];
List<String> myFollowingList = [];
try {
final QuerySnapshot following = await _firestore
.collection('following')
.doc(auth.getCurrentUser.uid)
.collection('userFollowing')
.get();
QuerySnapshot userfollowing = await _firestore
.collection('followers')
.doc(id)
.collection('userFollowers')
.get();
following.docs.forEach((element) {
myFollowingList.add(element.id);
});
userfollowing.docs.forEach((element) {
followingList.add(element.id);
});
// followingList.where((item) => myFollowingList.contains(item));
check(value) => myFollowingList.contains(value);
isFollowedByMe = followingList.any(check);
notifyListeners();
print(followingList);
print(myFollowingList);
} catch (err) {
print(err.toString() + 'this error is coming from profileState');
}
}
below code is how I build the follower/following list
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
UserModel? users;
#override
void initState() {
final profileState = Provider.of<ProfileState>(context, listen: false);
profileState.checkfollowers(context, widget.proFileId);
super.initState();
}
userComponent(UserModel? model, BuildContext context) {
final profileState = Provider.of<ProfileState>(context);
final auth = Provider.of<AuthState>(context);
return Container(
margin: EdgeInsets.symmetric(horizontal: 10),
padding: EdgeInsets.only(top: 10, bottom: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ProfilePage(profileId: '${model?.userId}'),
),
);
},
child: Row(
children: [
Container(
width: 60,
height: 60,
child: CircleAvatar(
radius: 50,
backgroundImage: NetworkImage('${model?.profilePic}'),
),
),
SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text('${model?.userName}',
style: TextStyle(
color: Colors.black, fontWeight: FontWeight.w500)),
SizedBox(
height: 5,
),
Text(
'${model?.displayName}',
style: TextStyle(
color: Colors.grey[500],
),
),
],
)
],
),
),
auth.getCurrentUser.uid == model?.userId
? Container()
: GestureDetector(
onTap: () {
if (auth.getCurrentUser.uid == model?.userId) {
print('you can not follow your self');
} else if (profileState.isFollowedByMe == true) {
profileState.setIsFollowedByMeToFalse();
profileState.handleUnFollow(context, model?.userId);
} else if (profileState.isFollowedByMe == false) {
profileState.setIsFollowedByMeToTrue();
profileState.handleFollow(context, model?.userId);
}
},
child: AnimatedContainer(
height: 35,
width: 110,
duration: Duration(milliseconds: 300),
decoration: BoxDecoration(
color: profileState.isFollowedByMe == true
? AppColors.white
: AppColors.pinkaccent,
borderRadius: BorderRadius.circular(5),
border: Border.all(
color: Colors.grey.shade700,
),
),
child: Center(
child: Text(
profileState.isFollowedByMe == true
? 'UnFollow'
: 'Follow',
style: TextStyle(
color: profileState.isFollowedByMe == true
? Colors.black
: Colors.white),
),
),
),
)
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.white,
title: Container(
height: 38,
child: TextField(
onChanged: (value) {},
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
contentPadding: EdgeInsets.all(0),
prefixIcon: Icon(
Icons.search,
color: Colors.grey.shade500,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(50),
borderSide: BorderSide.none),
hintStyle: TextStyle(fontSize: 14, color: Colors.grey.shade500),
hintText: "Search users"),
),
),
),
body: StreamListWrapper(
stream: _firestore
.collection('followers')
.doc(widget.proFileId)
.collection('userFollowers')
.snapshots(),
itemBuilder: (context, DocumentSnapshot snapshot) {
var data = snapshot.data() as Map<String, dynamic>;
users = UserModel.fromJson(data);
return userComponent(users, context);
},
text: '${widget.user?.userName} as no Followers',
),
);
It would be simpler (and more performant) to use the Set data structure rather than List. The intersection method on Set returns the items contained in both sets.
for example:
void main() {
Set<String> followingSet = {'Jeff', 'Mike', 'Joe', 'Jess'};
Set<String> myFollowingSet = {'Jess', 'Matt', 'Mike', 'Frank'};
Set<String> usersInBothSets = followingSet.intersection(myFollowingSet);
print(usersInBothSets); // {Mike, Jess}
}

Flutter: How to sync data from sqlite?

TextEditingController textEditingController = TextEditingController();
bool editState = false;
StatefulBuilder(
builder: (context, setState){
return AlertDialog(
content: SingleChildScrollView(
child: Column(
children: [
Container(
child: Padding(
padding: const EdgeInsets.only(bottom: 8, left: 8, right: 8),
child: Align(
alignment: Alignment.centerLeft,
child: editState ?
TextField(
controller: textEditingController,
) :
Text(checkpoint["memo"])
),
),
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: GestureDetector(
child: editState ? Text("Save") : Text("Edit"),
onTap: () async {
if(editState == true){
DBHelper dbHelper = DBHelper();
await dbHelper.updateCheckpoint(checkpoint["userkey"], checkpoint["id"], textEditingController.text);
}
else{
setState((){
editState = !editState;
});
}
},
),
),
],
),
)
],
),
)
);
},
)
This is my code. I want the Text (checkpoint ["memo"]) to be updated when I type something and click the save button.
I tried using ChangeNotifierProvider, but it didn't work. I may have used it incorrectly. So I don't know how to approach this problem. How can I solve this?
For text field there is one method onChanged(). Which gives you an updated text. You can save that text to some variable and use the same variable to display text where you want to display it. Don't forget to call setState() something like following in onChanged() to update the textfield text.
setState(() { _memo = newTextValue; });

firestore map to StreamBuilder => ListView.Builder

i want to show the songs list inside document (singer that user clicked on). Every song should load in list tile but all of them load in one tile.
and it loads the 'songs list' from all documents(all singers).
this is the FireStore DB
this is list of singers to choose from.
this should show only the songs from selected singer each one in a tile but shows all songs from all singers. and every singers song in one tile
class SongsList extends StatefulWidget {
#override
_SongsListState createState() => _SongsListState();
}
class _SongsListState extends State<SongsList> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: Firestore.instance.collection('singers').snapshots(),
builder: (
context,
snapshot,
) {
if (snapshot.data == null)
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.red,
valueColor: new AlwaysStoppedAnimation<Color>(Colors.teal),
),
);
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/back.png'), fit: BoxFit.contain)),
child: ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
var result = snapshot.data.documents[index]['songs list'];
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(
left: 10, right: 10, top: 10, bottom: 0),
child: Container(
height: 50,
width: 300,
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.white.withOpacity(0.5),
spreadRadius: 1.5,
blurRadius: 1.5,
//offset: Offset(0, 1), // changes position of shadow
),
],
borderRadius: BorderRadius.circular(5),
border: Border.all(
color: Colors.red[200],
width: 0.5,
style: BorderStyle.solid)),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
for (var res in result.entries)
Text(
res.key,
style: TextStyle(
fontSize: 20, color: Colors.red[500]),
),
]),
),
),
);
}),
);
},
),
);
}
}
If you want to get only the songs of one singer, then you need to specify the document id to retrieve one document, change this:
stream: Firestore.instance.collection('singers').snapshots(),
into this:
stream: Firestore.instance.collection('singers').document('aryana sayeed').snapshots(),
List tile has a corresponding index. I think you might have to build a list tile instead of a container. If you need a container, you have to write a code that would specifically get the singers name (documentID) wired on each container

Multiple streams to firebase documents without disposing

I am trying to add chatting to my app.
When my user starts a chat with a new user, I create a chatroom with a unique id in my Firebase database. I want my user to be notified with any updates of the chatroom document( eg: new messages) so when I create a chatroom, I also create a new stream to that chatroom document. Is there a problem with constantly listening to many documents with different streams without disposing the streams (because I want to get the latest results of any chatroom that user is a member of.)
This is my chatroom_screen code:
import 'package:flutter/material.dart';
import '../models/databse_management.dart';
class ChatroomScreen extends StatefulWidget {
static const String routeName = "/chatroom_screen";
#override
_ChatroomScreenState createState() => _ChatroomScreenState();
}
class _ChatroomScreenState extends State<ChatroomScreen> {
TextEditingController _messageTextEditingController = TextEditingController();
bool isChatroomExists;
Stream chatroomDocStream; //my initial stream variable which is null
#override
Widget build(BuildContext context) {
final Map<String, dynamic> passedArguments =
ModalRoute.of(context).settings.arguments;
//sedner details can be retrieved from currentUserDetails
final String senderId = passedArguments["senderId"];
final List<String> receiversIds = passedArguments["receiverIds"];
final String senderUsername = passedArguments["senderUsername"];
final List<String> receiverUsernames = passedArguments["receiverUsernames"];
final String chatroomId = passedArguments["chatroomId"];
if(chatroomDocStream == null){
chatroomDocStream = Firestore.instance.collection("chatrooms").document(chatroomId).snapshots(); //if no stream was created before (first time we build the widget), we connect a stream to this chatroom documentspecified with chatroomId
}
isChatroomExists = isChatroomExists == null
? passedArguments["isChatroomExists"]
: isChatroomExists;
final Image senderProfilePictureUrl =
passedArguments["senderProfilePicture"];
final List<Image> receiverProfilePictures =
passedArguments["receiverProfilePictures"];
final mediaQuery = MediaQuery.of(context).size;
final ThemeData theme = Theme.of(context);
//we get the values from the passed argument map
if (isChatroomExists) {
//load the previous chats
}
return Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
CircleAvatar(
backgroundImage: receiverProfilePictures[0]
.image, //right now only for 1 receiver but change it for multi members later
),
Container(
child: Text(receiverUsernames[0]),
margin: const EdgeInsets.only(left: 10),
),
],
),
),
body: //to do=> create a stream that is listening to the chatroom document for any changes
Stack(
children: <Widget>[
StreamBuilder(
//updates the chats whenever data of the chatroom document changes
stream: chatroomDocStream,
builder: (context, snapShot) {
return Column(
children: <Widget>[
Center(child: const Text("My chats"))
//message widgets go here
],
);
},
),
Positioned(
//positioned is used for positioning the widgets inside a stack. bottom: 10 means 10 pixel from bottom
bottom: 0,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
width: mediaQuery.width, //takes the total width of the screen
decoration: BoxDecoration(
color: theme.primaryColor.withOpacity(0.3),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
//always takes the remaining space (textField widget use this as its size https://stackoverflow.com/questions/45986093/textfield-inside-of-row-causes-layout-exception-unable-to-calculate-size)
child: Container(
padding: const EdgeInsets.symmetric(
horizontal:
10), //horizontal padding for the textfield widget
decoration: BoxDecoration(
color: theme.accentColor,
borderRadius: BorderRadius.circular(25),
border: Border.all(width: 2, color: theme.primaryColor),
),
child: TextField(
minLines: 1,
maxLines: 5,
controller: _messageTextEditingController,
decoration: const InputDecoration(
hintText: "Type something here...",
border: InputBorder
.none //removes all the border for textfield widget
),
),
),
),
Container(
child: Row(
//another row for buttons to be treated all toghether as a container in the parent row
children: <Widget>[
IconButton(
icon: const Icon(Icons.add),
onPressed: () {
//add media like image or pictures
},
),
IconButton(
icon: const Icon(Icons.camera_alt),
onPressed: () {
//take picture or video
},
),
IconButton(
icon: const Icon(Icons.send),
onPressed: () async {
if (_messageTextEditingController.text
.trim()
.isEmpty) {
return;
}
try {
await DatabaseManagement().sendMessage(
membersIds: [
senderId,
...receiversIds
], //extracts all the members of the list as seperate String items
//to do=>later on we have to get a list of the members as well
isChatroomExists: isChatroomExists,
chatroomId: chatroomId,
messageContent:
_messageTextEditingController.text,
senderId: senderId,
timestamp: Timestamp
.now(), //it's from firebase and records the time stamp of the sending message
);
if (isChatroomExists != true) {
isChatroomExists =
true; //after we sent a messsage, we 100% created the chatroom so it becomes true
}
} catch (e) {
print(
e.toString(),
);
return;
}
},
)
],
),
)
],
),
),
),
],
),
);
}
}
The idea is to have something like WhatsApp which you receive a notification of any updates in any chatroom you are a member of.
You can create a lot of snapshot listeners, but Google recommends a limit of 100 snapshot listeners per client:
Source: https://cloud.google.com/firestore/docs/best-practices?hl=en#realtime_updates

Resources