这是我的工厂。我可以从中访问数据并且可以正常工作:
factory ActivityFeedItem.fromDocument(DocumentSnapshot snap) {
var doc = snap.data();
return ActivityFeedItem(
username: doc['username'],
userId: doc['userId'],
type: doc['type'],
postId: doc['postId'],
userProfileImg: doc['userProfileImg'],
commentData: doc['commentData'],
timestamp: doc['timestamp'],
mediaUrl: doc['mediaUrl'],
);
}
另一方面,我想使用此方法将 postId
, userId
传递到另一个屏幕:
showPost(context) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PostScreen(
postId: postId,
userId: userId,
),
),
);
}
当我通过 userId
或 postId
时出现此错误:
Bad state: cannot get a field on a DocumentSnapshotPlatform that does not exist
这意味着当我尝试将数据传递到另一个屏幕时他找不到数据。 doc['userId'] 和 doc = snap.data, 这样就给出了 snap.data["userId], 并且我可以访问相同的 userId 或 PostID。
这是我的 ActivityFeed(Notifications) 类
class ActivityFeed extends StatefulWidget {
@override
_ActivityFeedState createState() => _ActivityFeedState();
}
class _ActivityFeedState extends State<ActivityFeed> {
// getActivityFeed() async {
// // QuerySnapshot snapshot = await activityFeedRef
// // .doc(currentUser.id)
// // .collection('feedItems')
// // .orderBy('timestamp', descending: true)
// // .limit(50).snapshots(),
// // .get();
// print(snapshot.docs.);
// List<ActivityFeedItem> feedItems = [];
// snapshot.docs.forEach((doc) {
// feedItems.add(ActivityFeedItem.fromDocument(doc));
// print('\nItem:');
// print('\nItem:');
// print('\nItem:');
// print('\nItem:');
// print('\nItem:');
// print('\nItem:');
// print('Activity Feed Item: ${doc.data}');
// debugPrint('Activity Feed Item: ${feedItems[1]}');
// print('\nItem:');
// print('\nItem:');
// print('\nItem:');
// print('\nItem:');
// print('\nItem:');
// print('\nItem:');
// print('\nItem:');
// });
// return feedItems;
// }
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.orange,
appBar: header(context, title: "Activity Feed"),
body: Container(
child: StreamBuilder<QuerySnapshot>(
stream: activityFeedRef
.doc(currentUser.id)
.collection('feedItems')
.orderBy('timestamp', descending: true)
.limit(50)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text("No Data");
}
List<ActivityFeedItem> feedItem = [];
snapshot.data.docs.forEach((doc) {
feedItem.add(ActivityFeedItem.fromDocument(doc));
});
// print(feedItem.length);
return ListView(
children: feedItem,
);
},
)),
);
}
}
Widget mediaPreview;
String activityItemText;
class ActivityFeedItem extends StatelessWidget {
final String username;
final String userId;
final String type; // 'like', 'follow', 'comment'
final String mediaUrl;
final String postId;
final String userProfileImg;
final String commentData;
final Timestamp timestamp;
ActivityFeedItem({
this.username,
this.userId,
this.type,
this.mediaUrl,
this.postId,
this.userProfileImg,
this.commentData,
this.timestamp,
});
factory ActivityFeedItem.fromDocument(DocumentSnapshot snap) {
var doc = snap.data();
return ActivityFeedItem(
username: doc['username'],
userId: doc['userId'],
type: doc['type'],
postId: doc['postId'],
userProfileImg: doc['userProfileImg'],
commentData: doc['commentData'],
timestamp: doc['timestamp'],
mediaUrl: doc['mediaUrl'],
);
}
showPost(context) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PostScreen(
postId: postId,
userId: userId,
),
),
);
}
configureMediaPreview(context) {
if (type == "like" || type == 'comment') {
mediaPreview = GestureDetector(
onTap: () => showPost(context),
child: Container(
height: 50.0,
width: 50.0,
child: AspectRatio(
aspectRatio: 16 / 9,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: CachedNetworkImageProvider(mediaUrl),
),
),
)),
),
);
} else {
mediaPreview = Text('');
}
if (type == 'like') {
activityItemText = "liked your post";
} else if (type == 'follow') {
activityItemText = "is following you";
} else if (type == 'comment') {
activityItemText = 'replied: $commentData';
} else {
activityItemText = "Error: Unknown type '$type'";
}
}
@override
Widget build(BuildContext context) {
configureMediaPreview(context);
return Padding(
padding: EdgeInsets.only(bottom: 2.0),
child: Container(
color: Colors.white54,
child: ListTile(
title: GestureDetector(
onTap: () => showProfile(context, profileId: userId),
child: RichText(
overflow: TextOverflow.ellipsis,
text: TextSpan(
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
),
children: [
TextSpan(
text: username,
style: TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(
text: ' $activityItemText',
),
]),
),
),
leading: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(userProfileImg),
),
subtitle: Text(
timeago.format(timestamp.toDate()),
overflow: TextOverflow.ellipsis,
),
trailing: mediaPreview,
),
),
);
}
}
showProfile(BuildContext context, {String profileId}) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Profile(
profileId: profileId,
),
),
);
}
这是我的个人资料屏幕
class Profile extends StatefulWidget {
final String profileId;
Profile({this.profileId});
@override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
final String currentUserId = currentUser?.id;
String postOrientation = "grid";
bool isFollowing = false;
bool isLoading = false;
int postCount = 0;
int followerCount = 0;
int followingCount = 0;
List<Post> posts = [];
@override
void initState() {
super.initState();
getProfilePosts();
getFollowers();
getFollowing();
checkIfFollowing();
}
checkIfFollowing() async {
DocumentSnapshot doc = await followersRef
.doc(widget.profileId)
.collection('userFollowers')
.doc(currentUserId)
.get();
setState(() {
isFollowing = doc.exists;
});
}
getFollowers() async {
QuerySnapshot snapshot = await followersRef
.doc(widget.profileId)
.collection('userFollowers')
.get();
setState(() {
followerCount = snapshot.docs.length;
});
}
getFollowing() async {
QuerySnapshot snapshot = await followingRef
.doc(widget.profileId)
.collection('userFollowing')
.get();
setState(() {
followingCount = snapshot.docs.length;
});
}
getProfilePosts() async {
setState(() {
isLoading = true;
});
QuerySnapshot snapshot = await postRef
.doc(widget.profileId)
.collection('usersPosts')
.orderBy('timestamp', descending: true)
.get();
setState(() {
isLoading = false;
postCount = snapshot.docs.length;
posts = snapshot.docs.map((doc) => Post.fromDocument(doc)).toList();
});
}
Column buildCountColumn(String label, int count) {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
count.toString(),
style: TextStyle(fontSize: 22.0, fontWeight: FontWeight.bold),
),
Container(
margin: EdgeInsets.only(top: 4.0),
child: Text(
label,
style: TextStyle(
color: Colors.grey,
fontSize: 15.0,
fontWeight: FontWeight.w400,
),
),
),
],
);
}
editProfile() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditProfile(currentUserId: currentUserId)));
}
Container buildButton({String text, Function function}) {
return Container(
padding: EdgeInsets.only(top: 2.0),
child: FlatButton(
onPressed: function,
child: Container(
width: 250.0,
height: 27.0,
child: Text(
text,
style: TextStyle(
color: isFollowing ? Colors.black : Colors.white,
fontWeight: FontWeight.bold,
),
),
alignment: Alignment.center,
decoration: BoxDecoration(
color: isFollowing ? Colors.white : Colors.blue,
border: Border.all(
color: isFollowing ? Colors.grey : Colors.blue,
),
borderRadius: BorderRadius.circular(5.0),
),
),
),
);
}
buildProfileButton() {
// viewing your own profile - should show edit profile button
bool isProfileOwner = currentUserId == widget.profileId;
if (isProfileOwner) {
return buildButton(
text: "Edit Profile",
function: editProfile,
);
} else if (isFollowing) {
return buildButton(
text: "Unfollow",
function: handleUnfollowUser,
);
} else if (!isFollowing) {
return buildButton(
text: "Follow",
function: handleFollowUser,
);
}
}
handleUnfollowUser() {
setState(() {
isFollowing = false;
});
// remove follower
followersRef
.doc(widget.profileId)
.collection('userFollowers')
.doc(currentUserId)
.get()
.then((doc) {
if (doc.exists) {
doc.reference.delete();
}
});
// remove following
followingRef
.doc(currentUserId)
.collection('userFollowing')
.doc(widget.profileId)
.get()
.then((doc) {
if (doc.exists) {
doc.reference.delete();
}
});
// delete activity feed item for them
activityFeedRef
.doc(widget.profileId)
.collection('feedItems')
.doc(currentUserId)
.get()
.then((doc) {
if (doc.exists) {
doc.reference.delete();
}
});
}
handleFollowUser() {
setState(() {
isFollowing = true;
});
// Make auth user follower of THAT user (update THEIR followers collection)
followersRef
.doc(widget.profileId)
.collection('userFollowers')
.doc(currentUserId)
.set({});
// Put THAT user on YOUR following collection (update your following collection)
followingRef
.doc(currentUserId)
.collection('userFollowing')
.doc(widget.profileId)
.set({});
// add activity feed item for that user to notify about new follower (us)
activityFeedRef
.doc(widget.profileId)
.collection('feedItems')
.doc(currentUserId)
.set({
"type": "follow",
"ownerId": widget.profileId,
"username": currentUser.username,
"userId": currentUserId,
"userProfileImg": currentUser.photoUrl,
"timestamp": timestamp,
});
}
buildProfileHeader() {
return FutureBuilder(
future: userRef.doc(widget.profileId).get(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
User user = User.fromDucument(snapshot.data);
return Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
CircleAvatar(
radius: 40.0,
backgroundColor: Colors.grey,
backgroundImage:
CachedNetworkImageProvider(user.photoUrl),
),
Expanded(
flex: 1,
child: Column(
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
buildCountColumn("posts", postCount),
buildCountColumn("followers", followerCount),
buildCountColumn("following", followingCount),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
buildProfileButton(),
],
),
],
),
),
],
),
Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(top: 12.0),
child: Text(
user.username,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
),
Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(top: 4.0),
child: Text(
user.displayName,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(top: 2.0),
child: Text(
user.bio,
),
),
],
),
);
});
}
buildProfilePosts() {
if (isLoading) {
return circularProgress();
} else if (posts.isEmpty) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SvgPicture.asset('assets/images/no_content.svg', height: 260.0),
Padding(
padding: EdgeInsets.only(top: 20.0),
child: Text(
"No Posts",
style: TextStyle(
color: Colors.redAccent,
fontSize: 40.0,
fontWeight: FontWeight.bold,
),
),
),
],
),
);
} else if (postOrientation == "grid") {
List<GridTile> gridTiles = [];
posts.forEach((post) {
gridTiles.add(GridTile(child: PostTile(post)));
});
return GridView.count(
crossAxisCount: 3,
childAspectRatio: 1.0,
mainAxisSpacing: 1.5,
crossAxisSpacing: 1.5,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
children: gridTiles,
);
} else if (postOrientation == "list") {
return Column(
children: posts,
);
}
}
setPostOrientation(String postOrientation) {
setState(() {
this.postOrientation = postOrientation;
});
}
buildTogglePostOrientation() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
onPressed: () => setPostOrientation("grid"),
icon: Icon(Icons.grid_on),
color: postOrientation == 'grid'
? Theme.of(context).primaryColor
: Colors.grey,
),
IconButton(
onPressed: () => setPostOrientation("list"),
icon: Icon(Icons.list),
color: postOrientation == 'list'
? Theme.of(context).primaryColor
: Colors.grey,
),
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: header(context, title: "Profile"),
body: ListView(
children: <Widget>[
buildProfileHeader(),
Divider(),
buildTogglePostOrientation(),
Divider(
height: 0.0,
),
buildProfilePosts(),
],
),
);
}
}