颤振:调用方法时返回空值

时间:2020-09-23 22:51:59

标签: flutter dart google-cloud-firestore

对于开发应用程序我是陌生的。我不断收到错误消息,导致对空调用'[]'方法。

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),

    );
  }
}

有人可以帮助我查询吗?

0 个答案:

没有答案