Trending Articles       10 Flutter Listview Examples       Flutter Custom Button Example       Flutter Audio Recording       Flutter 2.0 Autocomplete TextField       Current Location Google Maps

FireStore CRUD in Flutter - Social Application

In This post we are going to implement social posting application with FireStore database.

This Example we are implementing all CRUD operations.

What is Firestore Database?

Firestore is a real time database that stores collections and documents. A collection stores documents and a document is made up of json. There's no predefined data structure, you can add and remove fields as you please without having to do migration steps. It's known as a NoSQL database. This tutorial will not cover the way you plan your database for your application or what kind of structures you should use. We already have a Users collection, we'll add a new Posts collection where we will store the post data.

 

Let's get Started.

Step 1: Create Flutter Application.

Step 2: Integrate Firebase database Refer My Previous post Integrate Firebase with Flutter.

Step 3: Add required dependencies

dependencies:
  flutter:
    sdk: flutter
  cloud_firestore: ^0.13.4+2
  font_awesome_flutter: ^8.8.1
  image_picker: ^0.6.4
  firebase_storage: ^3.1.5

 

Step 4:

Create Homepage to display all posts.

SocialPostFireStore.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'add_post.dart';
import 'constants.dart';
import 'details_post.dart';

class SocialPostFireStore extends StatelessWidget {
  final db = Firestore.instance;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: app_color,
      appBar: AppBar(title: Text("Social App"),backgroundColor: app_color,elevation: 0.0,),
      floatingActionButton: FloatingActionButton(
        mini: true,
        backgroundColor: app_color,
        onPressed: (){
          Navigator.push(context, MaterialPageRoute(builder: (context)  {
            return AddPost();
          },));
        },
      child: Icon(Icons.add,color: Colors.white,),),
      body: ListView(
        padding: EdgeInsets.only(left: 5,right: 5,bottom: 20),
        children: [
          SizedBox(height: 5.0),
          StreamBuilder(

              stream: db.collection('Posts').orderBy("createdtm",descending: true).snapshots(),
              builder: (context, snapshot) {


                if (snapshot.hasData) {
                  return Column(
                    children: snapshot.data.documents.map((doc) {
                      print(doc.data);
                      return GestureDetector(
                        onTap: (){
                          Navigator.push(context, MaterialPageRoute(builder: (context)  {
                            return PostDetails(data:doc.data['post']);
                          },));
                        },
                        child: Card(
                          shape:RoundedRectangleBorder(
                            borderRadius: BorderRadius.all(Radius.circular(8)
                            ,)
                          ),
                          color: Colors.white,
                          elevation: 1,
                          shadowColor: Colors.amberAccent,
                          child: Padding(
                            padding: const EdgeInsets.all(8.0),
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                Row(
                                  mainAxisAlignment: MainAxisAlignment.start,
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  children: [
                                    Container(
                                      width: 40.0,
                                      height: 40.0,
                                      decoration: new BoxDecoration(
                                        shape: BoxShape.circle,
                                        image: new DecorationImage(
                                            fit: BoxFit.fill,
                                            image: new NetworkImage(
                                                "https://pbs.twimg.com/profile_images/916384996092448768/PF1TSFOE_400x400.jpg")),
                                      ),
                                      margin: const EdgeInsets.symmetric(horizontal: 8.0),
                                    ),
                                    (doc.data['post']==null)?Container():
                                    Expanded(child: Text(doc.data['post']['title'],style: TextStyle(color: app_color,fontSize: 18,fontWeight: FontWeight.bold,letterSpacing:1),))
                                  ],
                                ),
                                (doc.data['post']==null)?Container():
                                postInfo(doc.documentID,doc.data,context)
                              ],
                            ),
                          ),
                        ),
                      );
                      ListTile(
                        title: Text(doc.data['title'].toString()),

                        trailing: IconButton(
                          icon: Icon(Icons.cancel),
                          onPressed: () async {
                            await db
                                .collection('Posts')
                                .document(doc.documentID)
                                .delete();
                          },
                        ),
                      );
                    }).toList(),
                  );
                } else {
                  return SizedBox();
                }
              }),
        ],
      ),
    );
  }


  postInfo(documentID,data,context) {
    return Container(
      margin: EdgeInsets.symmetric(vertical: 10),
      child: Column(
        children: [
         Card(
           elevation: 0.2,
           margin: EdgeInsets.all(2),
           child: Image.network(data['post']['img'],fit: BoxFit.fitWidth,height: 120,),
         )  ,
         SizedBox(height: 10,),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 8),
            child: Text(data['post']['desc'],maxLines:3,overflow: TextOverflow.ellipsis,style: TextStyle(
              letterSpacing: 0.2,
              color: Colors.indigoAccent,fontSize: 14
            ),),
          ),
         SizedBox(height: 10,),
         Divider(height: 2,color: app_color,thickness: 1,),
         SizedBox(height: 10,),
         Container(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [

              Row(
                children: [
                  Padding(
                    padding: const EdgeInsets.only(left:8.0),
                    child: Row(
                      children: [
                        Text(data['post']['like'].toString(),style: TextStyle(color: Colors.amber),),
                        IconButton(

                            icon:  FaIcon(FontAwesomeIcons.solidHandPointUp,color: Colors.amber,),

                            onPressed: (){

                              data['post']['like']=  data['post']['like']+1;
                              db.collection("Posts").document(documentID).updateData(data);
                        }),
                      ],
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsets.only(left:8.0),
                    child: Row(
                      children: [
                        Text(data['post']['dislike'].toString(),style: TextStyle(color: Colors.orange),),
                        IconButton(

                            icon:  FaIcon(FontAwesomeIcons.solidHandPointDown,color: Colors.orange,),

                            onPressed: (){
                              data['post']['dislike']=  data['post']['dislike']+1;
                              db.collection("Posts").document(documentID).updateData(data).catchError((ex){print(ex);});
                            }),
                      ],
                    ),
                  ),
                ],
              ),
                PopupMenuButton(itemBuilder: (context) {
                  return >[

                    new PopupMenuItem( value: 2, child: new Text('Delete')),
                  ];
                },icon: Icon(Icons.more_vert,color: Colors.red,),onSelected: (value) => {
                db.collection("Posts").document(documentID).delete()
                },),

              ],
            ),
           decoration: new BoxDecoration(
             borderRadius: BorderRadius.only(topLeft: Radius.circular(10),bottomRight: Radius.circular(10)),
             border: Border.all(color: app_color,width: 1)
              ),

         )
        ],
      ),
    );
  }
}

 

Step 5: Create addPost file

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'constants.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart' as Path;

class AddPost extends StatefulWidget{
  var scaffole_key= GlobalKey();
  @override
  State createState() {
    // TODO: implement createState
    return AddPostState();
  }

}
class AddPostState extends State{
  final db = Firestore.instance;
  TextEditingController titleCtrl=TextEditingController();
  TextEditingController descCtrl=TextEditingController();
  String _uploadedFileURL;
  File  _image;

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      key: widget.scaffole_key,
      appBar: AppBar(elevation: 0.0,backgroundColor: app_color,title: Text("Add Post"),),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: ListView(
          children: [
            SizedBox(height: 10,),
            TextFormField(
              controller: titleCtrl,
              decoration: new InputDecoration(
                labelText: "Enter Post Title",
                fillColor: Colors.white,
                focusedBorder: OutlineInputBorder(
                  borderSide: BorderSide(color: app_color,width: 2),borderRadius: BorderRadius.only(bottomRight: Radius.circular(10),topLeft:  Radius.circular(10))
                ),
                border: new OutlineInputBorder(
                  borderRadius: new BorderRadius.circular(5.0),
                  borderSide: new BorderSide(
                  ),
                ),
                //fillColor: Colors.green
              ),
              validator: (val) {
                if(val.length==0) {
                  return "Title cannot be empty";
                }else{
                  return null;
                }
              },
              keyboardType: TextInputType.emailAddress,
              style: new TextStyle(
                fontFamily: "Poppins",
              ),
            ),
            SizedBox(height: 20,),
            TextFormField(
              minLines: 5,
              maxLines: 8,
              controller: descCtrl,

              decoration: new InputDecoration(
                focusedBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: app_color,width: 2),borderRadius: BorderRadius.only(bottomRight: Radius.circular(10),topLeft:  Radius.circular(10))
                ),
                labelText: "Enter Post Description",
                fillColor: Colors.white,

                border: new OutlineInputBorder(
                  borderRadius: new BorderRadius.circular(5.0),
                  borderSide: new BorderSide(
                  ),
                ),
                //fillColor: Colors.green
              ),
              validator: (val) {
                if(val.length==0) {
                  return "Description cannot be empty";
                }else{
                  return null;
                }
              },
              keyboardType: TextInputType.emailAddress,
              style: new TextStyle(
                fontFamily: "Poppins",
              ),
            ),

             SizedBox(height: 10,),
            Card(
              elevation: 0.2,
              margin: EdgeInsets.all(2),

            )  ,
            SizedBox(height: 10,),
            Container(
              padding: EdgeInsets.all(5),
              decoration: BoxDecoration(
                borderRadius: BorderRadius.only(bottomRight: Radius.circular(10),topLeft:  Radius.circular(10)),
                border: Border.all(color: Colors.amber,width: 1)
              ),
              child: Column(
                children: [
                  _image == null
                      ? IconButton(
                    iconSize: 40,
                    icon: Icon(Icons.camera_alt),
                    onPressed: chooseFile,
                    color: Colors.amber,
                  )
                      : IconButton(
                    iconSize: 20,
                    icon: Icon(Icons.clear),
                    onPressed: (){
                      setState(() {
                        _image=null;
                        _uploadedFileURL="";
                      });
                    },
                    color: Colors.red,
                  ),
                  Text('Selected Image'),
                  _image != null
                      ? Image.asset(
                    _image.path,
                    height: 150,
                  )
                      : Container(height: 150),


                ],
              ),
            ),
            SizedBox(height: 10,),
            Divider(height: 2,color: app_color,thickness: 1,),
            MaterialButton(
                color: app_color,
                textColor: Colors.white,
                child: Text("Submit"),
                onPressed: (){
if(titleCtrl.text.isEmpty)
  {
    widget.scaffole_key.currentState.showSnackBar(SnackBar(content: Text("Please enter Post title")));

    return ;
  }

              Mapposthm=new Map();
              Maphm=new Map();
              hm.putIfAbsent("title", () => titleCtrl.text);
              hm.putIfAbsent("desc", () => descCtrl.text);
              hm.putIfAbsent("id", () => "");
              hm.putIfAbsent("img", () => _uploadedFileURL);
              hm.putIfAbsent("dislike", () => 0);
              hm.putIfAbsent("like", () => 0);
              posthm.putIfAbsent("post", () => hm);
              posthm.putIfAbsent("createdtm", () => Timestamp.now());
            db.collection("Posts").reference().add(posthm);
               Navigator.pop(context);
            })


          ],
        ),
      ),
    );
  }
  Future chooseFile() async {
    await ImagePicker.pickImage(source: ImageSource.gallery).then((image) {
      setState(() {
        _image=image;
      });

      uploadFile(_image);
    });
  }

  Future uploadFile(_image) async {
    StorageReference storageReference = FirebaseStorage.instance
        .ref()
        .child('posts/${Path.basename(_image.path)}}');
    StorageUploadTask uploadTask = storageReference.putFile(_image);
    await uploadTask.onComplete;
    print('File Uploaded');
    storageReference.getDownloadURL().then((fileURL) {

      print("Uploaded file $fileURL");
      setState(() {
        _uploadedFileURL = fileURL;
      });


    });
  }

  Future delete(_image) async {
    StorageReference storageReference = FirebaseStorage.instance
        .ref()
        .child('posts/${Path.basename(_image.path)}}');
    storageReference.delete();

    print('File Uploaded');

  }
}

 

Step 6: Create Details page

import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';

import 'constants.dart';

class PostDetails extends StatelessWidget{
  PostDetails({this.data});
  var data;
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(elevation: 0.0,backgroundColor: app_color,),
      body: Padding(
        padding: const EdgeInsets.all(12.0),
        child: ListView(
          children: [
            Expanded(child: Text(data['title'],style: TextStyle(color: Colors.red,fontSize: 18,fontWeight: FontWeight.bold,letterSpacing:1),)),
            SizedBox(height: 10,),

            Card(
                        elevation: 0.2,
                        margin: EdgeInsets.all(2),
                        child: Image.network(data['img'],fit: BoxFit.fitWidth,),
                  ),
      SizedBox(height: 10,),
      Container(
      width: 200,
      decoration: BoxDecoration(border: Border.all(color: Colors.amber),borderRadius: BorderRadius.all(Radius.circular(5))),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          Padding(
            padding: const EdgeInsets.only(left:8.0),
            child: Row(
              children: [
                Text(data['like'].toString(),style: TextStyle(color: Colors.amber),),
                IconButton(

                  icon:  FaIcon(FontAwesomeIcons.solidHandPointUp,color: Colors.amber,),

                )
              ],
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(left:8.0),
            child: Row(
              children: [
                Text(data['dislike'].toString(),style: TextStyle(color: Colors.orange),),
                IconButton(

                  icon:  FaIcon(FontAwesomeIcons.solidHandPointDown,color: Colors.orange,),

                ),
              ],
            ),
          ),
        ],
      ),
    ),

                  SizedBox(height: 10,),
                  Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 8),
                  child: Text(data['desc'],style: TextStyle(
                      letterSpacing: 0.2,
                      color: Colors.indigoAccent,fontSize: 14
                  ),),
                  ),
                  SizedBox(height: 10,),
                  Divider(height: 2,color: app_color,thickness: 1,),


          ],
        ),
      ),
    );
  }

}

 

Step 7: Update main dart file

import 'package:flutter/material.dart';

import 'delete_firestore.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Social App',
      theme: ThemeData(

        primarySwatch: Colors.blue,
      ),
      home: SocialPostFireStore(),
    );
  }
}

 

Step 8: Run Application 


197 Views

Author: RRTutors

Flutter Questions

Android Questions