对于开发应用程序我是陌生的。我不断收到错误消息,导致对空调用'[]'方法。
activity_feed.dart
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:fluttershare/pages/post_screen.dart';
import 'package:fluttershare/pages/profile.dart';
import 'package:timeago/timeago.dart' as timeago;
import 'package:fluttershare/pages/home.dart';
import 'package:fluttershare/widgets/header.dart';
import 'package:fluttershare/widgets/progress.dart';
class ActivityFeed extends StatefulWidget {
@override
_ActivityFeedState createState() => _ActivityFeedState();
}
class _ActivityFeedState extends State<ActivityFeed> {
getActivityFeed() async {
QuerySnapshot snapshot = await activityRef
.document(currentUser.id)
.collection('feedItems')
.orderBy('timestamp', descending: true)
.limit(50)
.getDocuments();
List<ActivityFeedItem> feedItems = [];
snapshot.documents.forEach((doc) {
feedItems.add(ActivityFeedItem.fromDocument(doc));
});
return feedItems;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: header(context, titleText: 'Notifications'),
body: Container(
child: FutureBuilder(
future: getActivityFeed(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
return ListView(
children: snapshot.data,
);
},
),
),
);
}
}
Widget mediaPreview;
String activityItemText;
class ActivityFeedItem extends StatelessWidget {
final String username;
final String userId;
final String type;
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 doc) {
return ActivityFeedItem(
username: doc['username'],
userId: doc['userId'],
type: doc['type'],
mediaUrl: doc['mediaUrl'],
postId: doc['postId'],
userProfileImg: doc['userProfileImg'],
commentData: doc['commentData'],
timestamp: doc['timestamp'],
);
}
showPost(context) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PostScreen(
userId: userId,
postId: postId,
),
),
);
}
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,
),
),
);
}
在这里,showPost()函数正在创建错误
post_screen.dart
import 'package:flutter/material.dart';
import 'package:fluttershare/pages/home.dart';
import 'package:fluttershare/widgets/header.dart';
import 'package:fluttershare/widgets/post.dart';
import 'package:fluttershare/widgets/progress.dart';
class PostScreen extends StatelessWidget {
final String userId;
final String postId;
PostScreen({this.userId, this.postId});
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: postsRef
.document(userId)
.collection('userPosts')
.document(postId)
.get(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
Post post = Post.fromDocument(snapshot.data);
return Center(
child: Scaffold(
appBar: header(context, titleText: post.caption),
body: ListView(
children: <Widget>[
Container(
child: post,
)
],
),
),
);
},
);
}
}
错误日志
═╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building FutureBuilder<DocumentSnapshot>(dirty, state:
_FutureBuilderState<DocumentSnapshot>#45e49):
The method '[]' was called on null.
Receiver: null
Tried calling: []("postId")
The relevant error-causing widget was:
FutureBuilder<DocumentSnapshot>
package:fluttershare/pages/post_screen.dart:15
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1 DocumentSnapshot.[]
package:cloud_firestore/src/document_snapshot.dart:29
#2 new Post.fromDocument
package:fluttershare/widgets/post.dart:36
#3 PostScreen.build.<anonymous closure>
package:fluttershare/pages/post_screen.dart:25
#4 _FutureBuilderState.build
package:flutter/…/widgets/async.dart:740
#5 StatefulElement.build
package:flutter/…/widgets/framework.dart:4663
#6 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4546
#7 StatefulElement.performRebuild
package:flutter/…/widgets/framework.dart:4719
#8 Element.rebuild
package:flutter/…/widgets/framework.dart:4262
#9 BuildOwner.buildScope
package:flutter/…/widgets/framework.dart:2667
#10 WidgetsBinding.drawFrame
package:flutter/…/widgets/binding.dart:866
#11 RendererBinding._handlePersistentFrameCallback
package:flutter/…/rendering/binding.dart:286
#12 SchedulerBinding._invokeFrameCallback
package:flutter/…/scheduler/binding.dart:1117
#13 SchedulerBinding.handleDrawFrame
package:flutter/…/scheduler/binding.dart:1056
#14 SchedulerBinding._handleDrawFrame
package:flutter/…/scheduler/binding.dart:972
#18 _invoke (dart:ui/hooks.dart:253:10)
#19 _drawFrame (dart:ui/hooks.dart:211:3)
(elided 3 frames from dart:async)
Post.dart
import 'dart:async';
import 'package:animator/animator.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:fluttershare/models/user.dart';
import 'package:fluttershare/pages/activity_feed.dart';
import 'package:fluttershare/pages/comments.dart';
import 'package:fluttershare/pages/home.dart';
import 'package:fluttershare/widgets/progress.dart';
import 'custom_image.dart';
class Post extends StatefulWidget {
final String postId;
final String ownerId;
final String username;
final String location;
final String caption;
final String mediaUrl;
final dynamic likes;
Post({
this.postId,
this.ownerId,
this.username,
this.location,
this.caption,
this.mediaUrl,
this.likes,
});
factory Post.fromDocument(DocumentSnapshot doc){
return Post(
postId: doc['postId'] ,
ownerId: doc['ownerId'],
username: doc['username'],
location: doc['location'],
caption: doc['caption'],
mediaUrl: doc['mediaUrl'],
likes: doc['likes'],
);
}
int getLikeCount(likes){
if(likes == null){
return 0;
}
int count = 0;
likes.values.forEach((val){
if(val == true){
count+=1;
}
});
return count;
}
@override
_PostState createState() => _PostState(
postId: this.postId,
ownerId: this.ownerId,
username: this.username,
location: this.location,
caption: this.caption,
mediaUrl: this.mediaUrl,
likes: this.likes,
likeCount: getLikeCount(this.likes),
);
}
class _PostState extends State<Post> {
final String currentUserId = currentUser?.id;
final String postId;
final String ownerId;
final String username;
final String location;
final String caption;
final String mediaUrl;
Map likes;
int likeCount;
bool isLiked;
bool showHeart = false;
_PostState({
this.postId,
this.ownerId,
this.username,
this.location,
this.caption,
this.mediaUrl,
this.likes,
this.likeCount,
});
buildPostHeader(){
return FutureBuilder(builder: (context, snapshot){
if(!snapshot.hasData){
return circularProgress();
}
User user = User.fromDocument(snapshot.data);
bool isPostOwner = currentUserId == ownerId;
return ListTile(
leading: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(user.photoUrl),
backgroundColor: Colors.grey,
),
title: GestureDetector(
onTap: ()=> showProfile(context,profileId: user.id),
child: Text(user.username,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
subtitle: Text(location),
trailing: isPostOwner ? IconButton(icon: Icon(Icons.more_vert), onPressed: ()=> handleDelete(context),) : Text(''),
);
},
future: usersRef.document(ownerId).get(),
);
}
handleDelete(BuildContext parentContext){
return showDialog(context: parentContext,
builder: (context) {
return SimpleDialog(title: Text('Want to Delete?'),
children: <Widget>[
SimpleDialogOption(
onPressed: () {
Navigator.pop(context);
deletePost();
},
child: Text('Delete',
style: TextStyle(color: Colors.red),
),
),
SimpleDialogOption(
onPressed: ()=> Navigator.pop(context),
child: Text('Cancel',),
),
],
);
}
);
}
deletePost() async{
postsRef.document(ownerId).collection('userPosts')
.document(postId).get().then((doc) {
if(doc.exists){
doc.reference.delete();
}
});
storageref.child('post_$postId.jpg').delete();
QuerySnapshot actSnapshot = await activityRef.document(ownerId).collection('feedItems').where('postId', isEqualTo: postId).getDocuments();
actSnapshot.documents.forEach((doc) {
if(doc.exists){
doc.reference.delete();
}
});
QuerySnapshot comSnapshot =await commentsRef.document(postId).collection('comments').getDocuments();
comSnapshot.documents.forEach((doc) {
if(doc.exists){
doc.reference.delete();
}
});
}
buildPostImage(){
return GestureDetector(
onDoubleTap: ()=> handleLikes(),
child: Stack(
alignment: Alignment.center,
children: <Widget>[
cachedNetworkImage(mediaUrl),
showHeart ? Animator(
duration: Duration(milliseconds: 300),
tween: Tween(begin: 0.8, end: 1.4),
curve: Curves.elasticOut,
cycles: 0,
builder: (anim) => Transform.scale(scale: anim.value,
child: Icon(Icons.favorite, size: 80.0, color: Colors.pink[200] ,
),
),
) : Text(''),
],
),
);
}
handleLikes(){
bool _isLiked = likes[currentUserId] == true;
if(_isLiked){
postsRef.document(ownerId).collection('userPosts')
.document(postId)
.updateData({'likes.$currentUserId': false});
removeLikeFromActivity();
setState(() {
likeCount -= 1;
isLiked = false;
likes[currentUserId] = false;
});
}
else if(!_isLiked){
postsRef.document(ownerId).collection('userPosts')
.document(postId)
.updateData({'likes.$currentUserId': true});
addLikeToActivity();
setState(() {
likeCount += 1;
isLiked = true;
likes[currentUserId] = true;
showHeart = true;
});
Timer(Duration(milliseconds: 500),(){
setState(() {
showHeart = false;
});
});
}
}
addLikeToActivity(){
bool isNotPostOwner = currentUserId != ownerId;
if(isNotPostOwner) {
activityRef.document(ownerId).collection('feedItems')
.document(postId)
.setData({
'type': 'like',
'username': currentUser.username,
'userId': currentUser.id,
'userProfileImg': currentUser.photoUrl,
'postId': postId,
'mediaUrl': mediaUrl,
'timestamp': timestamp,
});
}
}
removeLikeFromActivity(){
bool isNotPostOwner = currentUserId != ownerId;
if(isNotPostOwner) {
activityRef.document(ownerId).collection('feedItems')
.document(postId)
.get()
.then((doc) {
if (doc.exists) {
doc.reference.delete();
}
});
}
}
buildPostFooter(){
return Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(padding: EdgeInsets.only(top: 40.0,left: 20.0),
),
GestureDetector(
onTap: handleLikes,
child: Icon(
isLiked ? Icons.favorite : Icons.favorite_border,
size: 28.0,
color: Colors.pink,
),
),
Padding(padding: EdgeInsets.only(right: 20.0,),
),
GestureDetector(
onTap: ()=> showComments(
context, postId: postId, ownerId: ownerId, mediaUrl: mediaUrl
),
child: Icon(
Icons.chat,
size: 28.0,
color: Colors.blue[900],
),
),
],
),
Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 20),
child: likeCount == 1 ? Text('$likeCount like',style: TextStyle(color: Colors.black,fontWeight: FontWeight.bold),): Text('$likeCount likes',style: TextStyle(color: Colors.black,fontWeight: FontWeight.bold),),
),
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 20),
child: Text('$username',style: TextStyle(color: Colors.black,fontWeight: FontWeight.bold,),
),
),
Expanded(
flex: 1,
child: Text(' ' + caption),
),
],
),
],
);
}
showComments(BuildContext context,{String postId, String ownerId, String mediaUrl}){
Navigator.push(context, MaterialPageRoute(builder: (context){
return Comments(
postId: postId,
postOwnerId: ownerId,
postMediaUrl: mediaUrl,
);
})
);
}
@override
Widget build(BuildContext context) {
isLiked = (likes[currentUserId] == true);
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
buildPostHeader(),
buildPostImage(),
buildPostFooter(),
],
);
}
}
该错误是在post_screen.dart上引起的。但是可以从另一个文件中调用post_screen,而不会发生任何错误。这是该文件:
post_tile.dart
import 'package:flutter/material.dart';
import 'package:fluttershare/pages/post_screen.dart';
import 'package:fluttershare/widgets/custom_image.dart';
import 'package:fluttershare/widgets/post.dart';
class PostTile extends StatelessWidget {
final Post post;
PostTile(this.post);
showPost(context){
Navigator.push(
context, MaterialPageRoute(
builder: (context) => PostScreen(
postId: post.postId,
userId: post.ownerId,),
),
);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: ()=> showPost(context),
child: cachedNetworkImage(post.mediaUrl),
);
}
}
有人可以帮助我查询吗?