Flutter Python: Firebase like backend using Python
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!
- Install Python(https://www.python.org)
- Install PyCharm(https://www.jetbrains.com/pycharm)
- Install MongoDB(https://www.mongodb.com)
- Install postman(https://www.getpostman.com)
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