type 'Future<dynamic>' is not a subtype of type 'Widget?' - firebase

IM trying to call on Tap method, what I want now is change it to a future method because on Tap the method is not ready because im calling a firebase query . So heres first my method
where the onTap function is
child: InkWell(
onTap: () async {
setState(() {
israting = true;
});
},
child: israting
? buildBody(videos.data()['likes'], videos.data()['id'])
: Icon(
Icons.star,
size: 37,
color: videos.data()['likes'].contains(uid)
? Colors.yellow
: Colors.white,
),
),
So im giving the buildBody 2 parameters from a streambuilder
and then thats the function
buildBody(videoid, video) async {
if (videoid.contains(uid)) {
await FirebaseFirestore.instance
.collection("videos")
.doc(video)
.collection("uservotes")
.doc(uid)
.get()
.then((value) {
votefromfirebase = value.data()["rating"];
});
return Container(
child: Stack(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(3, 7, 0, 0),
child: Align(
alignment: Alignment.topLeft,
child: RatingBarIndicator(
rating: votefromfirebase,
itemBuilder: (context, index) => InkWell(
child: Icon(
Icons.star,
color: Colors.amber,
),
),
itemCount: 5,
itemSize: 31.0,
direction: Axis.horizontal,
),
),
),
Align(
alignment: Alignment.topRight,
child: TextButton(
onPressed: () {
print(video);
letuservoting = true;
setState(() {
_userRating = 0.0;
israting = false;
});
dislike(idovvideo, _userRating);
},
child: Text(
"Clear",
style: TextStyle(color: Colors.white, fontSize: 20),
),
),
),
],
));
} else {
return Container(
child: Stack(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(3, 7, 0, 0),
child: Align(
alignment: Alignment.topLeft,
child: RatingBarIndicator(
rating: _userRating,
itemBuilder: (context, index) => InkWell(
onTap: () {
setState(() {
_userRating = index + 1.toDouble();
});
likevideo(video, _userRating);
},
child: Icon(
Icons.star,
color: Colors.amber,
),
),
itemCount: 5,
itemSize: 31.0,
direction: Axis.horizontal,
),
),
),
Align(
alignment: Alignment.topRight,
child: TextButton(
onPressed: () {
letuservoting = true;
setState(() {
_userRating = 0.0;
israting = false;
});
dislike(idovvideo, _userRating);
},
child: Text(
"Clear",
style: TextStyle(color: Colors.white, fontSize: 20),
),
),
),
],
),
);
}
}
buildprofile(String url) {
return Container(
width: 50,
height: 50,
child: Stack(
children: [
Positioned(
left: (50 / 2) - (50 / 2),
child: Container(
width: 50,
height: 50,
padding: EdgeInsets.all(1),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(25),
),
child: Container(
child: ClipRRect(
borderRadius: BorderRadius.circular(60),
child: Container(
height: 110,
width: 110,
decoration: BoxDecoration(
color: Colors.white,
),
child: url != null && url != "profilepictureer"
? Image.network(
url,
fit: BoxFit.cover,
)
: Image.asset(
'assets/profilepictureer.png') // Your widget is here when image is no available.
),
),
decoration: new BoxDecoration(
shape: BoxShape.circle,
border: new Border.all(color: Colors.black, width: 4)),
),
),
),
],
),
);
}
}
At the moment im getting this error
The following _TypeError was thrown building:
type 'Future<dynamic>' is not a subtype of type 'Widget?'
When the exception was thrown, this was the stack
#0 _VideopageofcurrentuserState.build.<anonymous closure>.<anonymous closure> (package:wichtigdenyady/taking%20videos/currentuservideos.dart:310:47)
#1 SliverChildBuilderDelegate.build
package:flutter/…/widgets/sliver.dart:455
#2 SliverMultiBoxAdaptorElement._build
package:flutter/…/widgets/sliver.dart:1201
#3 SliverMultiBoxAdaptorElement.performRebuild.processElement
package:flutter/…/widgets/sliver.dart:1145
#4 Iterable.forEach (dart:core/iterable.dart:257:30)
The error throws in the onTap function

The buildBody function cannot be an async function.
You should move the call to Firebase out of this function and use the result of it in state, instead.
An async function always returns a Future. Because the return type isn't defined, it assumes it will return a Future<dynamic>. If you defined the return type as Widget buildBody(videoid, video) async, the IDE would show an error.
In your onTap function, you can make the call:
onTap: () async {
setState(() {
israting = true;
});
if(!videos.data()['likes'].contains(uid)) return; // don't call firebase if uid is not in videoid
final value = await FirebaseFirestore.instance
.collection("videos")
.doc(video)
.collection("uservotes")
.doc(uid)
.get();
setState(() {
votefromfirebase = value.data()["rating"];
});
},
Next, change the buildBody function to a non-async function and check if votefromfirebase is in the state
Widget buildBody(videoid, video) {
if (votefromfirebase == null) {
return Container(); // return empty container, progress indicator, or anything else
}
// else return the normal body
return Container(
child: Stack(
// rest of this widget

Related

How to update and delete a data in a list according to it's document id - flutter, firebase 2021

I am trying to delete and update a list of details in flutter. For that i used doc('document_id') which was given as a solution in another stackoverflow question. I tried some another solutions given in stacker flow too. But nothing fork for me. But if I give a specific documentID I am able to delete that. Also how can I pass the selected data to update page too.
class addressProfile extends StatefulWidget {
const addressProfile({Key? key}) : super(key: key);
#override
_addressProfileState createState() => _addressProfileState();
}
class _addressProfileState extends State<addressProfile> {
var Default = 'unDefault';
delete() async {
try {
FirebaseFirestore.instance
.collection("address")
.doc('document_id')
.delete();
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey.shade100,
appBar: AppBar(
centerTitle: true,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
title: Text(
'My Addresses',
style: TextStyle(color: Colors.black),
),
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
color: Colors.black,
),
onPressed: () {
Navigator.of(context).pushNamed('/profilePage');
},
),
),
body: ListView(
padding: EdgeInsets.all(16),
children: [
StreamBuilder<QuerySnapshot>(
stream:
FirebaseFirestore.instance.collection("address").snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Expanded(
child: SizedBox(
height: 700,
child: ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
QueryDocumentSnapshot x = snapshot.data!.docs[index];
return Container(
child: Card(
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(x['firstName']),
Text(' '),
Text(x['lastName']),
],
),
Text(""),
Row(
children: [
Text(x['primaryPhone']),
Text(" / "),
Text(x['secondaryPhone']),
],
),
Text(''),
Row(
children: [
Text(x['address1']),
Text(', '),
Text(x['address2']),
Text(', '),
Text(x['city']),
Text(', '),
Text(x['region']),
],
),
Divider(
color: Colors.black,
),
Row(
children: [
Container(
child: Radio(
value: 'default',
groupValue: Default,
onChanged: (String? val) {
setState(() {
if (val != null)
Default = val;
});
}),
),
Container(
child: Text("Default"),
),
Container(
padding: EdgeInsets.only(left: 60),
child: Align(
child: ElevatedButton.icon(
onPressed: () {
if (snapshot.data!.docs.length >
1) {
delete();
Fluttertoast.showToast(
msg:
"Address deleted successfully",
toastLength:
Toast.LENGTH_SHORT,
gravity:
ToastGravity.BOTTOM,
textColor: Colors.black,
backgroundColor:
Colors.green.shade400,
);
} else {
Fluttertoast.showToast(
msg:
"Main address cannot be deleted",
toastLength:
Toast.LENGTH_SHORT,
gravity:
ToastGravity.BOTTOM,
textColor: Colors.black,
backgroundColor:
Colors.green.shade400,
);
}
},
label: Text('Delete'),
style: ElevatedButton.styleFrom(
fixedSize: Size(90, 20),
primary: Colors.red.shade500,
padding: EdgeInsets.symmetric(
horizontal: 5,
),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(
10))),
icon: Icon(
Icons.delete_outline_sharp),
),
),
),
Container(
padding: EdgeInsets.only(left: 14),
child: Align(
child: ElevatedButton.icon(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) =>
updateAddress(),
),
);
},
label: Text('Update'),
style: ElevatedButton.styleFrom(
fixedSize: Size(90, 20),
primary:
Colors.green.shade500,
padding: EdgeInsets.symmetric(
horizontal: 5,
),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(
10))),
icon: Icon(Icons.edit),
),
),
),
],
),
],
),
),
),
);
},
),
),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
Align(
alignment: AlignmentDirectional.bottomCenter,
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pushNamed('/addNewAddress');
},
child: Text(
"Add New Address",
style: TextStyle(
fontSize: 15,
letterSpacing: 2,
color: Colors.black,
),
),
style: ElevatedButton.styleFrom(
fixedSize: Size(250, 40),
primary: Colors.green.shade500,
padding: EdgeInsets.symmetric(
horizontal: 50,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10))),
),
),
],
),
);
}
}
This is so far I did. Please help me to continue.
I don't know how you have saved the data. But I got an issue like this and what I did was, I added a variable as "id" to database while saving the data. There is an auto generated id plugin for flutter (nanoid). You can add that and save the data as following.
var id = nanoid(10) //10 is the length of the id. You can give as you wish
create() async {
try {
FirebaseFirestore.instance
.collection("address")
.doc(id)
.set({
"id":id,
//other inputs
});
} catch (e) {
print(e);
}
}
Then you can use that id as a key to update ad delete.
For example according to you code to delete you can use like this in the onPress(){} of delete button,
FirebaseFirestore.instance.collection("address").doc(x['id']).delete();
So the data related to id will be deleted.
Also better to use proper name rather than "x".
Can you please try this
delete(String docId) async {
try {
FirebaseFirestore.instance
.collection("address")
.doc(docId)
.delete();
} catch (e) {
print(e);
}
}
Your delete function call
delete(snapshot.data!.docs[index].id);
Update document
void update(String docId){
FirebaseFirestore.instance.collection("address").doc(docId) .update({"field1":"fieldValue1","field2":"fieldValue2"});
}
Let me know if you find any issues in comment

How to validate image in flutter on pressing submit button?

My every textfield is getting validated. But if the image is not selected and I press submit. It successfully gets uploaded to firestore without an image. But I want to make it stop incase image is null. When i load image it gets display in buildGridView. I guess i need to apply logic somewhere here. That if buildGridView is null. Stop or something. How can i achieve it. Thanks
Widget AddPost() {
return Form(
key: _key,
autovalidate: _validate,
child: Padding(
padding: const EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 0.0),
child: Column(
children: <Widget>[
_getPropertyTypeDropDown(),
_getPropertyTypeDetailDropDown(),
UploadPropertyImages(),
Container(
margin: EdgeInsets.only(left: 7),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
width: 200,
height: MediaQuery.of(context).size.height / 4,
//color: Colors.green,
child: buildGridView(),
),
RaisedButton(
child: Text("Submit"),
onPressed: () async {
if (_key.currentState.validate()) {
_key.currentState.save();
Alert(
context: context,
style: alertStyle,
type: AlertType.info,
title: "YEY !!",
desc: "Your Ad will be displayed soon.",
buttons: [
DialogButton(
child: Text(
"Thankyou",
style: TextStyle(color: Colors.white, fontSize: 20),
),
// onPressed: () => Navigator.pop(context),
color: Color.fromRGBO(0, 179, 134, 1.0),
radius: BorderRadius.circular(0.0),
),
],
).show();
await runMyFutureGetImagesReference();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RoleCheck()));
} else {
setState(() {
_validate = true;
});
}
},
textColor: Colors.black,
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
splashColor: Colors.grey,
),
],
),
),
//_showSubmitButton(),
],
)),
);
}
Widget buildGridView() {
return GridView.count(
crossAxisCount: 3,
children: List.generate(images.length, (index) {
Asset asset = images[index];
print(asset.getByteData(quality: 100));
return Padding(
padding: EdgeInsets.all(8.0),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15)),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.blueAccent, width: 2)),
child: AssetThumb(
asset: asset,
width: 300,
height: 300,
),
),
),
// ),
);
}),
);
}
Widget UploadPropertyImages() {
return Container(
child: Center(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
NiceButton(
width: 250,
elevation: 8.0,
radius: 52.0,
text: "Select Images",
background: Colors.blueAccent,
onPressed: () async {
List<Asset> asst = await loadAssets();
if (asst.length == 0) {
showInSnackBar("No images selected");
}
// SizedBox(height: 10,);
else {
showInSnackBar('Images Successfully loaded');
}
}),
],
),
)));
}
Widget build(BuildContext context) {
return Scaffold(
// backgroundColor: Colors.grey[600],
key: _scaffoldKey,
body: Container(
padding: EdgeInsets.all(16.0),
child: Form(
child: ListView(
shrinkWrap: true,
children: <Widget>[
Center(
child: Text(
"Post New Ad",
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
),
),
AddPost(),
Padding(
padding: EdgeInsets.fromLTRB(0, 0, 0, 16),
),
], //:TODO: implement upload pictures
),
),
),
);
}
Future<List<Asset>> loadAssets() async {
List<Asset> resultList = List<Asset>();
String error = "No error Detected";
try {
resultList = await MultiImagePicker.pickImages(
maxImages: 10,
enableCamera: true,
selectedAssets: images,
cupertinoOptions: CupertinoOptions(takePhotoIcon: "chat"),
materialOptions: MaterialOptions(
actionBarColor: "#abcdef",
actionBarTitle: "Upload Image",
allViewTitle: "All Photos",
useDetailsView: false,
selectCircleStrokeColor: "#000000",
),
);
print(resultList.length.toString() + "it is result list");
/* print((await resultList[0].getThumbByteData(122, 100)));
print((await resultList[0].getByteData()));
print((await resultList[0].metadata));*/
print("loadAssets is called");
} on Exception catch (e) {
error = e.toString();
print(error.toString() + "on catch of load assest");
}
By default Image Picker field is not a form element. You can pick a plugin to do so for your application. Here I am adding one. Please include it and it will give you the scope to validate the picked image as your requirements.
https://pub.dev/packages/image_picker_form_field
You will have to include it in pubspec.yaml and you are ready to use the widget it provides. Just use that like below:
ImagePickerFormField(
child: Container(
height: 40,
child: Center(child: Text("Select Photo")),
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8)),
border: Border.all(
color: Theme.of(context).disabledColor, width: 1)),
),
previewEnabled: true,
autovalidate: true,
context: context,
onSaved: (File value) {
print("on saved called");
},
validator: (File value) {
if (value == null)
return "Please select a photo!";
else return null; },
initialValue: null, //File("some source")
)

Download data from firebase Firestore to flutter

I am using Firestore as a database for a Chat in one part of the application that I am building. I have a collection on my Firestore that looks like this:
messages(collection)
userId
userId-otherUserId(sub-collection)
randomId
content : String,
timestamp : Date
...
and I would like to retrieve firstly all of the userIs-otherUserId in a ListView and then to retrieve the lastMessage by retrieving the last randomId(and in that randomId the last message is the last element with key 'content') and I would like it to look something like this.
Prototype
where the Loading represents last message but the name and profilePicture I can get from my API.
The pictures of my Firestore database:
First Image
Second Image
Third Image
Is there any chance that I could save the name and Profile picture on the database in the userId-otherUserId(collection)?
The code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:dio/dio.dart';
import 'package:disby_app/const.dart';
import 'package:disby_app/data/request_model.dart';
import 'package:disby_app/global_widgets/loading_indicator.dart';
import 'package:disby_app/main_screens/main_chat_and_request_screens/chat.dart';
import 'package:disby_app/main_screens/main_chat_and_request_screens/profile_details_screen.dart';
import 'package:disby_app/public/keys.dart';
import 'package:disby_app/services/json_requests.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../global_widgets/chat_and_requests_placeholder.dart';
class MainChatScreen extends StatefulWidget {
#override
_MainChatScreenState createState() => _MainChatScreenState();
}
class _MainChatScreenState extends State<MainChatScreen> {
bool _areThereChats = false;
bool _isLoading = true;
String groupChatId;
String userId = '1';
List<String> listOfLastMessages = [];
List<String> listOfIds = [];
List<ArrayOfRequestsModel> arrayOfRequests = [];
final GlobalKey<AnimatedListState> _listKey = GlobalKey();
Future readLocal() async {
var prefs = await SharedPreferences.getInstance();
userId = prefs.getString(kUserId) ?? '';
print(userId);
setState(() {});
}
Future<void> _getRequests() async {
final prefs = await SharedPreferences.getInstance();
final userId = prefs.getString(kUserId);
try {
await Dio().post(baseUrl + acceptedUsers + "/" + userId).then((snapshot) {
print(snapshot);
final Map<String, dynamic> response = snapshot.data;
print(response);
if (response['response'] == 'success') {
List<dynamic> arrayFromServer = response['acceptedUsers'];
if (arrayFromServer.isNotEmpty) {
arrayFromServer.forEach((userData) {
arrayOfRequests.add(ArrayOfRequestsModel.fromJson(userData));
listOfIds.add(userData['userId']);
print(userData['userId']);
});
_areThereChats = true;
_isLoading = false;
getMessages(userId);
} else {
_areThereChats = false;
_isLoading = false;
}
setState(() {});
} else {
_areThereChats = false;
_isLoading = false;
setState(() {});
}
});
} catch (e) {
print(e);
}
}
getMessages(String userId) {
int i = 0;
print(listOfIds.length);
print(listOfIds);
listOfIds.forEach((element) {
Firestore.instance
.collection('messages')
.document('$userId')
.collection('$userId-$element')
.orderBy('timestamp', descending: true)
.limit(3)
.getDocuments()
.then((QuerySnapshot snapshot) {
print(snapshot.documents[0].data['content']);
snapshot.documents.forEach((f) {
print(f.data['content']);
listOfLastMessages.add(f.data['content']);
setState(() {});
i++;
});
if (i == listOfIds.length) {
setState(() {});
print(listOfLastMessages);
}
});
});
}
#override
void initState() {
readLocal();
_getRequests();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Container(
width: double.infinity,
height: double.infinity,
child: SingleChildScrollView(
// child: (userId != '1')
// ? StreamBuilder(
// stream: Firestore.instance
// .collection('messages')
// .document('5ef4d83175b07cf074525c08')
// .snapshots(),
// builder: (context, snapshot) {
// // if (snapshot.hasData) {
// if (!snapshot.hasData) {
// return Center(
// child: CircularProgressIndicator(
// valueColor: AlwaysStoppedAnimation<Color>(themeColor),
// ),
// );
// } else {
// return Container(
// width: 100,
// height: 200,
// child: ListView.builder(
// padding: EdgeInsets.all(10.0),
// itemBuilder: (context, index) =>
// buildItem(context, snapshot.data.documents),
// itemCount: snapshot.data.documents.length,
// ),
// );
// }
// },
// )
// : LoadingIndicator(
// loading: _isLoading,
// ),
// ),
child: Column(
children: <Widget>[
(_isLoading == false)
? (_areThereChats == true)
? Container(
child: AnimatedList(
key: _listKey,
shrinkWrap: true,
initialItemCount: arrayOfRequests.length,
itemBuilder:
(BuildContext context, index, animation) {
return _buildItem(context, arrayOfRequests[index],
animation, index);
},
),
)
: ChatAndRequestPlaceholder(
text: translate('when_someone_sends_you_a_request'))
: LoadingIndicator(
loading: _isLoading,
),
],
),
),
),
);
}
Widget buildItem(BuildContext context, DocumentSnapshot document) {
if (document['id'] == userId) {
return Container();
} else {
return Container(
child: FlatButton(
child: Row(
children: <Widget>[
Flexible(
child: Container(
child: Column(
children: <Widget>[
Container(
child: Text(
'Nickname: ${document['content']}',
style: TextStyle(color: primaryColor),
),
alignment: Alignment.centerLeft,
margin: EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 5.0),
),
Container(
child: Text(
'About me: ${document['content'] ?? 'Not available'}',
style: TextStyle(color: primaryColor),
),
alignment: Alignment.centerLeft,
margin: EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 0.0),
)
],
),
margin: EdgeInsets.only(left: 20.0),
),
),
],
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Chat(
peerId: document.documentID,
peerAvatar: document['photoUrl'],
),
),
);
},
color: greyColor2,
padding: EdgeInsets.fromLTRB(25.0, 10.0, 25.0, 10.0),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
),
margin: EdgeInsets.only(bottom: 10.0, left: 5.0, right: 5.0),
);
}
}
_buildItem(BuildContext context, ArrayOfRequestsModel arrayOfSentRequests,
Animation animation, int index) {
return GestureDetector(
onTap: () {
print(arrayOfSentRequests.profilePictureUrl);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Chat(
peerId: arrayOfSentRequests.userId,
peerAvatar: arrayOfSentRequests.profilePictureUrl,
),
),
);
},
child: Container(
height: 82,
child: ScaleTransition(
scale: animation,
child: Padding(
padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Color.fromRGBO(0, 0, 0, 0.07),
offset: Offset(0, 5),
blurRadius: 11,
),
],
),
width: double.infinity,
height: 78,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Container(
height: double.infinity,
child: GestureDetector(
onTap: () {
Navigator.of(context, rootNavigator: true).push(
MaterialPageRoute(
builder: (context) => ProfileDetailsScreen(),
),
);
},
child: Row(
children: <Widget>[
SizedBox(width: 24),
ClipRRect(
borderRadius: BorderRadius.circular(25),
child: Image(
image: NetworkImage(
arrayOfSentRequests.profilePictureUrl,
),
width: 48,
),
),
SizedBox(width: 10),
Expanded(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
arrayOfSentRequests.nameAndSurname,
style: TextStyle(
color: Colors.black,
fontFamily: 'Avenir',
fontWeight: FontWeight.w700,
fontSize: 18,
),
),
Text(
'11/11/2020', //arrayOfSentRequests.nameAndSurname
style: TextStyle(
color: Colors.grey,
fontFamily: 'Avenir',
fontWeight: FontWeight.w500,
fontSize: 15,
),
),
],
),
),
Container(
child: Text(
// (arrayOfSentRequests.lastMessage !=
// null)
// ? arrayOfSentRequests.lastMessage
// : 'Loading..',
(listOfLastMessages.isNotEmpty)
? listOfLastMessages[index]
.toString()
: 'Loading...',
maxLines: 2,
softWrap: true,
style: TextStyle(
color: Colors.black54,
fontFamily: 'Avenir',
fontSize: 15,
),
),
),
],
),
),
),
SizedBox(width: 10),
],
),
),
),
),
Container(
child: Center(
child: Icon(
Icons.keyboard_arrow_right,
size: 22,
color: Colors.black54,
),
),
),
SizedBox(width: 10),
],
),
),
),
),
),
);
}
}

How to perform CRUD operations with BLoC pattern?

I was learning on flutter how to perform CRUD operations using BLoC pattern, I saw a tutorial online and try to apply it. C,R,U are OK, but deletion is not working. this is the source code.
Please Help !
file todo_bloc.dart
import 'package:zencartos/models/todo.dart';
import 'package:zencartos/repository/todo_repository.dart';
class TodoBloc {
//Get instance of the Repository
final _todoRepository = TodoRepository();
final _todoController = StreamController<List<Todo>>.broadcast();
get todos => _todoController.stream;
TodoBloc() {
getTodos();
}
getTodos({String query}) async {
_todoController.sink.add(await _todoRepository.getAllTodos(query: query));
}
addTodo(Todo todo) async {
await _todoRepository.insertTodo(todo);
getTodos();
}
updateTodo(Todo todo) async {
await _todoRepository.updateTodo(todo);
getTodos();
}
deleteTodoById(int id) async {
_todoRepository.deleteTodoById(id);
getTodos();
}
dispose() {
_todoController.close();
}
}
DAO Page todo_dao.dart
import 'package:zencartos/database/database.dart';
import 'package:zencartos/models/todo.dart';
class TodoDao {
final dbProvider = DatabaseProvider.dbProvider;
Future<int> createTodo(Todo todo) async{
final db = await dbProvider.database;
var result = db.insert(todoTABLE, todo.toDatabaseJson());
return result;
}
Future<List<Todo>> getTodos({List<String> columns, String query})
async{
final db = await dbProvider.database;
List<Map<String, dynamic>> result;
if (query != null){
if (query.isNotEmpty)
result = await db.query(todoTABLE, columns: columns, where: 'description LIKE ?', whereArgs: ["%$query%"]);
} else {
result = await db.query(todoTABLE, columns: columns);
}
List<Todo> todos = result.isNotEmpty
? result.map((item)=> Todo.fromDatabaseJson(item)).toList()
: [];
return todos;
}
//Update Todo record
Future<int> updateTodo(Todo todo) async{
final db = await dbProvider.database;
var result = await db.update(todoTABLE, todo.toDatabaseJson(),
where: "id = ?", whereArgs: [todo.id]);
return result;
}
//Delete Todo records
Future<int> deleteTodo(int id) async{
final db = await dbProvider.database;
var result = await db.delete(todoTABLE, where: 'id = ?', whereArgs: [id]);
return result;
}
}
Database Page database.dart
import 'dart:io';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
final todoTABLE = 'Todo';
class DatabaseProvider {
static final DatabaseProvider dbProvider = DatabaseProvider();
Database _database;
Future<Database> get database async {
if (_database != null) return _database;
_database = await createDatabase();
return _database;
}
createDatabase() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "ReactiveTodo");
var database = await openDatabase(path, version: 1, onCreate: initDB, onUpgrade: onUpgrade);
return database;
}
void onUpgrade(Database database, int oldVersion, int newVersion){
if (newVersion > oldVersion){}
}
void initDB(Database database, int version) async{
await database.execute("CREATE TABLE $todoTABLE ("
"id INTEGER PRIMARY KEY, "
"description TEXT, "
"is_done INTEGER "
")");
}
}
Model page todo.dart
class Todo{
int id;
String description;
bool isDone = false;
Todo({this.id, this.description, this.isDone = false});
factory Todo.fromDatabaseJson(Map<String, dynamic> data) => Todo(
id: data['data'],
description: data['description'],
isDone: data['is_done'] == 0 ? false : true,
);
Map<String, dynamic> toDatabaseJson() => {
"id": this.id,
"description": this.description,
"is_done": this.isDone == false ? 0 : 1,
};
}
View page home_Page2.dart
import 'package:flutter/services.dart';
import 'package:zencartos/bloc/todo_bloc.dart';
import 'package:zencartos/models/todo.dart';
class HomePage2 extends StatelessWidget {
HomePage2({Key key, this.title}) : super(key: key);
final TodoBloc todoBloc = TodoBloc();
final String title;
//Allows Todo card to be dismissable horizontally
final DismissDirection _dismissDirection = DismissDirection.horizontal;
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark.copyWith(
statusBarColor: Colors.white,
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.dark,
statusBarBrightness: Brightness.dark));
return Scaffold(
resizeToAvoidBottomPadding: false,
body: SafeArea(
child: Container(
color: Colors.white,
padding:
const EdgeInsets.only(left: 2.0, right: 2.0, bottom: 2.0),
child: Container(
//This is where the magic starts
child: getTodosWidget()))),
bottomNavigationBar: BottomAppBar(
color: Colors.white,
child: Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(color: Colors.grey, width: 0.3),
)),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
icon: Icon(
Icons.menu,
color: Colors.indigoAccent,
size: 28,
),
onPressed: () {
//just re-pull UI for testing purposes
todoBloc.getTodos();
}),
Expanded(
child: Text(
"Todo",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontFamily: 'RobotoMono',
fontStyle: FontStyle.normal,
fontSize: 19),
),
),
Wrap(children: <Widget>[
IconButton(
icon: Icon(
Icons.search,
size: 28,
color: Colors.indigoAccent,
),
onPressed: () {
_showTodoSearchSheet(context);
},
),
Padding(
padding: EdgeInsets.only(right: 5),
)
])
],
),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: Padding(
padding: EdgeInsets.only(bottom: 25),
child: FloatingActionButton(
elevation: 5.0,
onPressed: () {
_showAddTodoSheet(context);
},
backgroundColor: Colors.white,
child: Icon(
Icons.add,
size: 32,
color: Colors.indigoAccent,
),
),
));
}
void _showAddTodoSheet(BuildContext context) {
final _todoDescriptionFormController = TextEditingController();
showModalBottomSheet(
context: context,
builder: (builder) {
return new Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: new Container(
color: Colors.transparent,
child: new Container(
height: 230,
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(10.0),
topRight: const Radius.circular(10.0))),
child: Padding(
padding: EdgeInsets.only(
left: 15, top: 25.0, right: 15, bottom: 30),
child: ListView(
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextFormField(
controller: _todoDescriptionFormController,
textInputAction: TextInputAction.newline,
maxLines: 4,
style: TextStyle(
fontSize: 21, fontWeight: FontWeight.w400),
autofocus: true,
decoration: const InputDecoration(
hintText: 'I have to...',
labelText: 'New Todo',
labelStyle: TextStyle(
color: Colors.indigoAccent,
fontWeight: FontWeight.w500)),
validator: (String value) {
if (value.isEmpty) {
return 'Empty description!';
}
return value.contains('')
? 'Do not use the # char.'
: null;
},
),
),
Padding(
padding: EdgeInsets.only(left: 5, top: 15),
child: CircleAvatar(
backgroundColor: Colors.indigoAccent,
radius: 18,
child: IconButton(
icon: Icon(
Icons.save,
size: 22,
color: Colors.white,
),
onPressed: () {
final newTodo = Todo(
description:
_todoDescriptionFormController
.value.text);
if (newTodo.description.isNotEmpty) {
/*Create new Todo object and make sure
the Todo description is not empty,
because what's the point of saving empty
Todo
*/
todoBloc.addTodo(newTodo);
//dismisses the bottomsheet
Navigator.pop(context);
}
},
),
),
)
],
),
],
),
),
),
),
);
});
}
void _showTodoSearchSheet(BuildContext context) {
final _todoSearchDescriptionFormController = TextEditingController();
showModalBottomSheet(
context: context,
builder: (builder) {
return new Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: new Container(
color: Colors.transparent,
child: new Container(
height: 230,
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(10.0),
topRight: const Radius.circular(10.0))),
child: Padding(
padding: EdgeInsets.only(
left: 15, top: 25.0, right: 15, bottom: 30),
child: ListView(
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextFormField(
controller: _todoSearchDescriptionFormController,
textInputAction: TextInputAction.newline,
maxLines: 4,
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.w400),
autofocus: true,
decoration: const InputDecoration(
hintText: 'Search for todo...',
labelText: 'Search *',
labelStyle: TextStyle(
color: Colors.indigoAccent,
fontWeight: FontWeight.w500),
),
validator: (String value) {
return value.contains('#')
? 'Do not use the # char.'
: null;
},
),
),
Padding(
padding: EdgeInsets.only(left: 5, top: 15),
child: CircleAvatar(
backgroundColor: Colors.indigoAccent,
radius: 18,
child: IconButton(
icon: Icon(
Icons.search,
size: 22,
color: Colors.white,
),
onPressed: () {
/*This will get all todos
that contains similar string
in the textform
*/
todoBloc.getTodos(
query:
_todoSearchDescriptionFormController
.value.text);
//dismisses the bottomsheet
Navigator.pop(context);
},
),
),
)
],
),
],
),
),
),
),
);
});
}
Widget getTodosWidget() {
return StreamBuilder(
stream: todoBloc.todos,
builder: (BuildContext context, AsyncSnapshot<List<Todo>> snapshot) {
return getTodoCardWidget(snapshot);
},
);
}
Widget getTodoCardWidget(AsyncSnapshot<List<Todo>> snapshot) {
if (snapshot.hasData) {
return snapshot.data.length != 0
? ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, itemPosition) {
Todo todo = snapshot.data[itemPosition];
final Widget dismissibleCard = new Dismissible(
background: Container(
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
"Deleting",
style: TextStyle(color: Colors.white),
),
),
),
color: Colors.redAccent,
),
onDismissed: (direction) {
todoBloc.deleteTodoById(todo.id);
},
direction: _dismissDirection,
key: new ObjectKey(todo),
child: Card(
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.grey[200], width: 0.5),
borderRadius: BorderRadius.circular(5),
),
color: Colors.white,
child: ListTile(
leading: InkWell(
onTap: () {
//Reverse the value
todo.isDone = !todo.isDone;
todoBloc.updateTodo(todo);
},
child: Container(
//decoration: BoxDecoration(),
child: Padding(
padding: const EdgeInsets.all(15.0),
child: todo.isDone
? Icon(
Icons.done,
size: 26.0,
color: Colors.indigoAccent,
)
: Icon(
Icons.check_box_outline_blank,
size: 26.0,
color: Colors.tealAccent,
),
),
),
),
title: Text(
todo.description,
style: TextStyle(
fontSize: 16.5,
fontFamily: 'RobotoMono',
fontWeight: FontWeight.w500,
decoration: todo.isDone
? TextDecoration.lineThrough
: TextDecoration.none),
),
)),
);
return dismissibleCard;
},
)
: Container(
child: Center(
//this is used whenever there 0 Todo
//in the data base
child: noTodoMessageWidget(),
));
} else {
return Center(
child: loadingData(),
);
}
}
Widget loadingData() {
todoBloc.getTodos();
return Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
Text("Loading...",
style: TextStyle(fontSize: 19, fontWeight: FontWeight.w500))
],
),
),
);
}
Widget noTodoMessageWidget() {
return Container(
child: Text(
"Start adding Todo...",
style: TextStyle(fontSize: 19, fontWeight: FontWeight.w500),
),
);
}
dispose() {
todoBloc.dispose();
}
}
repository page todo_repository.dart
import 'package:zencartos/models/todo.dart';
class TodoRepository {
final todoDao = TodoDao();
Future getAllTodos({String query}) => todoDao.getTodos(query: query);
Future insertTodo(Todo todo) => todoDao.createTodo(todo);
Future updateTodo(Todo todo) => todoDao.updateTodo(todo);
Future deleteTodoById(int id) => todoDao.deleteTodo(id);
//We are not going to use this in the demo
//Future deleteAllTodos() => todoDao.deleteAllTodos();
}
In your todo_bloc.dart, your delete method should be:
deleteTodoById(int id) async {
await _todoRepository.deleteTodoById(id);
getTodos();
}

Displaying Firebase Firestore Listview Data as a list Flutter

I am currently able to display a Listview filled with data from my Firestore database. My current problem is, that I want to make it dissmissable, so I need to be able to use functions such as:
setState(() {
items.removeAt(index);
});
Now, I read up on how to generate a list, but none of the examples mention a firebase Streambuilder like I am using. So I was just wondering if it was possible to make the data into a list? And if not, if there are any other ways to make a firestore listview dissmissable? Here is how I currently get the data:
Container(
child: StreamBuilder(
stream: Firestore.instance.collection('users').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(themeColor),
),
);
} else {
return ListView.builder(
scrollDirection: Axis.vertical,
padding: EdgeInsets.all(10.0),
itemBuilder: (context, index) => buildItem(context, snapshot.data.documents[index]),
itemCount: snapshot.data.documents.length,
);
}
},
),
),
Thanks in advance, any help is appreciated.
Builditem looks like this:
Widget buildItem(BuildContext context, DocumentSnapshot document) {
if (document['id'] == currentUserId || document['gender'] == null) {
return Container();
}
if (currentUserPreference == 'male' && currentUserGender == 'male') {
return showGayMales(document);
}
And the ShowGayMales method looks like this:
Widget showGayMales(DocumentSnapshot document) {
if (document['id'] == currentUserId || document['id'] == nopeId || ) {
return Container();
} else {
return Container(
child: Slidable(
delegate: new SlidableScrollDelegate(),
actionExtentRatio: 0.3,
child: Card(
child: Padding(
padding:EdgeInsets.fromLTRB(20.0, 10.0, 25.0, 10.0),
child: Row(
children: <Widget>[
Material(
color: Colors.transparent,
child: Icon(
FontAwesomeIcons.male,
color: textColor,
),
),
new Flexible(
child: Container(
child: new Column(
children: <Widget>[
new Container(
child: Text(
'${document['aboutMe']}',
style: TextStyle(color: textColor, fontSize: 30.0),
),
alignment: Alignment.centerLeft,
margin: new EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 5.0),
),
new Container(
child: Row(
children: <Widget>[
Text(
'-'+'${document['nickname'] ?? 'Not available'}',
style: TextStyle(color: textColor, fontSize: 15.0, fontWeight: FontWeight.bold),
),
Text(
','+' ${document['age'] ?? ''}'
)
],
),
alignment: Alignment.centerLeft,
margin: new EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 0.0),
)
],
),
margin: EdgeInsets.only(left: 20.0),
),
),
],
),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
),
actions: <Widget>[
new IconSlideAction(
caption: 'Not interested!',
color: errorColor,
icon: Icons.clear,
onTap: () => notinterested('${document['id']}'),
),
],
secondaryActions: <Widget>[
new IconSlideAction(
caption: "Interested!",
color: primaryColor,
icon: Icons.check,
onTap: () => interested('${document['nickname']}', '${document['id']}', '${document['gender']}', '${document['aboutMe']}', '${document['age']}', '${document['preference']}'),
),
],
),
margin: EdgeInsets.only(bottom: 10.0, left: 5.0, right: 5.0),
);
}
}
You can fetch Firestore data and add it to a List by mapping it to an Object first.
List<Users> userList;
Future<void> getUsers() async {
userList = [];
var collection = FirebaseFirestore.instance.collection('users');
collection.get().then((value) {
value.docs.forEach((users) {
debugPrint('get Users ${users.data()}');
setState(() {
// Map users.data to your User object and add it to the List
userList.add(User(User.setUserDetails(users.data())));
});
});
});
}
// Let's say this is User object
class User {
var username;
User(User doc) {
this.username = doc.getUsername();
}
getUsername() => username;
// fetch name using Firestore field name
User.setUserDetails(Map<dynamic, dynamic> doc)
: username = doc['name'];
}

Resources