# Building a social media app with AWS Amplify and Flutter(PART 3)

In [PART 1](https://phatrabbitapps.com/building-a-social-media-app-with-aws-amplify-and-flutterpart-1) and [PART 2](https://phatrabbitapps.com/building-a-social-media-app-with-aws-amplify-and-flutterpart-2) of this series, we covered how to configure Amplify with Flutter, create login/registration screens and update profile screen.
<br>
In this post, we'll look at how to let a user add and display all posts in the database in real time.
<br>
<h3> POST</h3>
As always, everything concerning the post entity is in the post branch on the [Github page](Please get the complete code here [Friends_GitHUB](https://github.com/trey-rosius/friends/tree/post). 
<br>

The next major component in our system after user login is creating and displaying a Post. </br>
A post consist of a  `postText`, `postStatus`, `postImage`, and the user who made the post.
</br>
For the `postText`, we'll use a `TextFormField` to grab user input, and then, we'll use this `image_picker: ^0.8.2` library we already added to the `pubspec.yaml` file.
`createPost function`

```
Future<bool> createPost(String userId) async{
    loading = true;
    /**
     * first retrieve user model
     */
    try {
      List<User> user = await Amplify.DataStore.query(
          User.classType, where: User.ID.eq(userId));
      print("user details" + user[0].toString());
      Post post = Post(content: postTextController.text.trim(),
          type: PostType.TEXT_IMAGE,
          status: PostStatus.CREATED,
          userID: userId,
          postImageUrl: postImageUrl,
          user: user[0],
          //user model
          createdOn: TemporalDateTime.now(),
          updatedOn: TemporalDateTime.now());
      await Amplify.DataStore.save(post);
      loading = false;
      return true;
    }catch(ex){
      print(ex.toString());
      loading = false;
      return false;
    }
  }
``` 
What the code above does is, firstly, it gets the details of the user(UserModel) who's about to make the post. Then, create a new post Object and adds all the corresponding `Post` fields. 
<br>
<br>
Remember that there's a one-to-many relationship between a `User` and a `Post`.
<br>
<br>
So when saving a post item, we have to satisfy that relationship.
<br>
<br>
We do that by adding the `userId` and `user[0]` object to the `Post` object.
<br>
We call `await Amplify.DataStore.save(post);` to save the Post Object to the datastore, which would later synchronize this local data with our remote database. You don't have to worry about that.
<h3> Getting a List of All Posts</h3>
After saving a post, the next obvious step is to display all posts in our Posts database.
<br>
From the Official Amplify Docs
<br>
<br>
Predicates
Predicates are filters that can be used to match items in the DataStore. When applied to a query(), they constrain the returned results. When applied to a save(), they act as a pre-requisite for updating the data. You can match against fields in your schema by using the following predicates:

Strings: `eq | ne | le | lt | ge | gt | contains | notContains | beginsWith | between`

Numbers: `eq | ne | le | lt | ge | gt | between`

Lists: `contains | notContains`


So We'll be using predicates to filter and grab the data we need

`getAllPosts`
```
 Future<List<Post>>queryAllPosts() async{
    List<Post> posts = await Amplify.DataStore.query(Post.classType,sortBy: [Post.CREATEDON.descending()]);
    return posts;
  }
``` 
We want to retrieve the latest posts first. So we sortBy `[Post.CREATEDON.descending()]` <br>

Pagination is also a piece of cake. Here's a function to paginate through the posts
<br>

`paginatePosts`
```
 Future<List<Post>>paginatePosts(int page) async{
    List<Post> posts = await Amplify.DataStore.query(Post.classType,sortBy: [Post.CREATEDON.descending()], 
                 pagination: QueryPagination(page:page, limit:100));
    return posts;
  }
``` 
`getSinglePost`


```
 Future<List<Post>>getSinglePost(String postID) async{
    List<Post> posts = await Amplify.DataStore.query(Post.classType,where: Post.ID.eq(postID));
    return posts;
  }

``` 
`updatePost`

```
  Future<void> updatePost(String postId) async{
    Post oldPost = (await Amplify.DataStore.query(Post.classType,
        where: Post.ID.eq(postId)))[0];
    Post newPost = oldPost.copyWith(id: oldPost.id,
        createdOn: TemporalDateTime.now(),
        updatedOn: TemporalDateTime.now());

    await Amplify.DataStore.save(newPost);
  }
``` 
You can also get all posts for a particular user

`queryAllUserPosts`

```
  Future<List<Post>>queryAllUserPosts(String userId) async{
    List<Post> posts = await Amplify.DataStore.query(Post.classType,
        where: Post.USERID.eq(userId),
        sortBy: [Post.CREATEDON.descending()]);
    return posts;
  }
``` 
Now, how do we display a list of posts on our home page?
<br>
Like so

```
postRepo.queryAllPosts().then((value) {
        postRepo.posts = value;
        print("something posts "+value.toString());

      });
``` 
Firstly, we'll query all the posts and save them in a List, then we loop through the list and display each individual post.

```
ListView.builder(


            itemBuilder: (context,index){
              return PostItem(userId!,postRepo.posts[index]);
            },itemCount: postRepo.posts.length,) 
``` 



<h3> Real Time Posts</h3>
GraphQL supports subscriptions, that allow us to retrieve data in realtime when a query or mutation occurs.
<br>
Amplify gives us the ability to subscribe to changes on our Models.
<br>
In the above scenario when we created a post, on the home screen, we had to hit refresh in order to view the newly created post in our listview. 
<br>
With subscriptions, we no longer need a refresh. The post would automatically be retrieved and added to our listview.
<br>

How cool is that ?
![COOL](https://media.giphy.com/media/xjZtu4qi1biIo/giphy.gif)

So how do we accomplish this ?

```
late StreamSubscription postStream;
      postStream = Amplify.DataStore.observe(Post.classType).listen((event) {

       if(event.eventType != EventType.create){
          return;
        }
        if(postRepo.posts[0].id != event.item.id){

          postRepo.posts.insert(0, event.item);
          print('Received event of type ' + event.eventType.toString());
          print('Received post ' + event.item.toString());

        }





      });
``` 
From the above code, we define a StreamSubscription and listen to all events happening in the POST model.
<br>
You can use `event.eventType` to know if the event was an `onCreate`, `onUpdate` or `onDelete`.
<br>
At this stage, we want to get updates for `create` events only.
<br>
I noticed that subscriptions get fired multiple times, which wasn't what I wanted. It added duplicates to my listview. 
<br>
So I added this if statement `if(postRepo.posts[0].id != event.item.id)` to make sure not to add duplicates to the listview. 
<br>
Please get the complete code here [Friends_GitHUB](https://github.com/trey-rosius/friends/tree/post) and it out.
<br>
Here's also a video illustration.

<a href="https://www.loom.com/share/f2413d7956bb450eb641e8f8541940ef">
    <p>14 October, 2021 - Create/List Post - Watch Video</p>
    <img style="max-width:300px;" src="https://cdn.loom.com/sessions/thumbnails/f2413d7956bb450eb641e8f8541940ef-with-play.gif">
  </a>

In the next post, we'll be going through creating and getting a list of comments for a post in real-time. <br>
So stay tuned 😉
Thanks for checking this out.<br>
I'll love to know what you think about this series or how you feel about the Amplify Framework.
<br>
Happy Coding ✌🏾
