Flutter Python: Firebase like backend using Python

Published by Abhay Rastogi on

Create a rest API using Python with MongoDB, then notify changes through WebSocket in a flutter. In this flutter python blog, we will implement Rxdart for making HTTP request Reactive.

In this, we will not deal with model classes in Flutter as I tried to make it as like as the firebase database backend.

Before diving into code make sure of installing the following for backend!

Create a new project in Pycharm then Install dependencies in python project.in this project, we will use Aiohttp server for python

pip install aiohttp
pip install pymongo
pip install bson
pip install python-socketio

Websocket notify any changes made in the database, data will be pass-through socket.emit() and socket.on() will listen.

  • socket.emit() to emit data to an event.
  • socket.on() get data from emit.

Create new python file api.py and check if the server is running properly!

from aiohttp import web
import json

async def apitest(request):
    result ={"status":"200"}
    return web.Response(text=json.dumps(result), status=200)

app = web.Application()
app.router.add_get('/apitest',apitest)
web.run_app(app)

Type terminal python api.py to run this file then check http://localhost:8080/apitest in postman

As server is running properly, let’s add MongoDB in and create two functions one for adding data in database and second one for fetching data.

from aiohttp import web
import json
import pymongo
from bson import json_util

mongourl = "mongodb://localhost:27017/" #Database url

async def apitest(request):
    result ={"status":"200"}
    return web.Response(text=json.dumps(result), status=200)

async def create(request):
    try:
        collection = request.match_info.get('collection', "Anonymous") #Get collection name from params

        myclient = pymongo.MongoClient(mongourl)
        mydb = myclient["database"]  # Database Name
        mycol = mydb["collection"]  # Collection name
        j = await request.json()
        x = mycol.insert_one(j)
        
        # emit something is changed notify event through webSocket
        await sio.emit('notify',str(x.inserted_id))
        
        response_obj = {'status': 'success',"_id":str(x.inserted_id)}
        return web.Response(text=json.dumps(response_obj), status=200)
    except Exception as e:
        response_obj = {'status': 'failed', 'reason': str(e)}
        web.Response(text=json.dumps(response_obj), status=500)

async def show(request):
    try:
        collection = request.match_info.get('collection', "Anonymous") #Get collection name from params

        myclient = pymongo.MongoClient(mongourl)
        mydb = myclient["database"]  # Database Name
        mycol = mydb["collection"]  # Collection name
        mydoc = list(mycol.find())  # add all data in a List
        # convert list into json
        response_obj = json.loads(json_util.dumps(mydoc))
        res = {"Data":response_obj}
        return web.Response(text=json.dumps(res), status=200)
    except Exception as e:
        response_obj = {'status': 'failed', 'reason': str(e)}
        web.Response(text=json.dumps(response_obj), status=500)

app = web.Application()

#Add Routers
app.router.add_get('/apitest',apitest)
app.router.add_post('/create/{collection}',create)
app.router.add_get('/show/{collection}',show)

if __name__ == '__main__':
    web.run_app(app)

Now create new flutter project and install these dependencies in pubspec.yaml

rxdart: ^0.23.1
http: ^0.12.0+3
socket_io_client: ^0.9.7+2

create service.dart and make sure of giving the exact hosted IP address of your machine before port address.

import 'package:rxdart/rxdart.dart';
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
import 'package:socket_io_client/socket_io_client.dart' as IO;

class Service{

  IO.Socket socket =IO.io('http://192.168.0.5:8080' ,<String, dynamic>{
    'transports': ['websocket']
  }) ;
  String initialCount = ''; //if the data is not passed by paramether it initializes with ''
  BehaviorSubject<String> _subjectCounter;

  Service({this.initialCount}){
   #Show emited data on notify event through websocket! 
   socket.on('notify', (data) {
      print(data);
      httpRequest();

    });
    
 _subjectCounter = new BehaviorSubject<String>.seeded(this.initialCount); //initializes the subject with element already
 
 }

  Stream<String> get counterObservable => _subjectCounter.stream;

  void increment(){
    httpPostRequest();
    httpRequest();
  }



#Get User Data
  void httpRequest() async{
    var url = 'http://192.168.0.5:8080/show/user';
    // Await the http get response, then decode the json-formatted response.
    var response = await http.get(url);
    if (response.statusCode == 200) {
      var jsonResponse = convert.jsonDecode(response.body);
      var itemCount = jsonResponse;
      _subjectCounter.sink.add(response.body);
      print('Number of books about http: $jsonResponse.');
    } else {
      print('Request failed with status: ${response.statusCode}.');
    }

  }


#Post User Data
  void httpPostRequest() async{
    var url = 'http://192.168.0.5:8080/create/user';

    Map data = {
      "firstName": "CodeSearch",
      "lastName": "Online"
    };
    var body = convert.json.encode(data);
    Map userHeader = {"Content-type": "application/json", "Accept": "application/json"};
    // Await the http get response, then decode the json-formatted response.
    var response = await http.post(url,body:body,headers:{"Content-Type": "application/json"});
    if (response.statusCode == 200) {
      var jsonResponse = convert.jsonDecode(response.body);
      var itemCount = jsonResponse;


      print('Number of books about http: $jsonResponse.');
    } else {
      print('Request failed with status: ${response.statusCode}.');
    }

  }



  void dispose(){
    _subjectCounter.close();
  }

}

Show data in list view in main.dart!

import 'package:flutter/material.dart';
import 'package:newsapp/CounterBloc.dart';
import 'package:newsapp/service.dart';
import 'dart:convert' as convert;
void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {


  Service _service = new Service();


  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
        ),
        body: new Center(
          child:   new StreamBuilder(stream: _service.counterObservable, builder: (context, AsyncSnapshot<String> snapshot){


            Map<String, dynamic> user = convert.jsonDecode(snapshot.data);
            var rest1 = user["Data"];
//            var rest = user["Data"][0];
//            print('Data In Zero Index, ${rest['firstName']}!');
             print(snapshot.data);
            return  new ListView.builder
              (
                itemCount: rest1.length as int,
                itemBuilder: (BuildContext ctxt, int index) {
{
                  return new Text(rest[index]['firstName']);

                }

            );
          }),
        ),
        floatingActionButton: new Column(mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[
          new Padding(padding: EdgeInsets.only(bottom: 10), child:
          new FloatingActionButton(
            onPressed: _service.increment,
            tooltip: 'Increment',
            child: new Icon(Icons.add),
          )
          ),
          new FloatingActionButton(
            onPressed: _service.decrement,
            tooltip: 'Decrement',
            child: new Icon(Icons.remove),
          ),
        ])
    );
  }

  @override
  void initState() {
   _service.httpRequest();
    // TODO: implement initState
    super.initState();
  }

  @override
  void dispose() {
    _service.dispose();
    super.dispose();
  }

}

1 Comment

Cesar Gonzalez · 27th January 2020 at 9:28 pm

very, very nice. I’ll make a try

Leave a Reply

Avatar placeholder

Your email address will not be published.