Manage and preview content of your mobile app in web browser with real time updates

The first step will be to create a nodeJS backend for our web application.

mkdir broker
cd broker
npm install express firebase dotenv cors
npm install nodemon --save-dev
npm install body-parser
#express server config
PORT=8080
HOST=localhost
HOST_URL=http://localhost:8080/
#firebase database config
API_KEY=AIzaSyCzlZ51QzbIeR0excyX1tvyOOiFB_hn8J0
AUTH_DOMAIN=preview-mobile-via-web.firebaseapp.com
PROJECT_ID=preview-mobile-via-web
STORAGE_BUCKET=preview-mobile-via-web.appspot.com
MESSAGING_SENDER_ID=735635784528
APP_ID=1:735635784528:web:314561c2b7b2c57aa8a255
‘use strict’;
const dotenv = require(‘dotenv’);
const assert = require(‘assert’);
dotenv.config();
const {
PORT,
HOST,
HOST_URL,
API_KEY,
AUTH_DOMAIN,
PROJECT_ID,
STORAGE_BUCKET,
MESSAGING_SENDER_ID,
APP_ID,
} = process.env;
assert(PORT, ‘PORT is required’);
assert(HOST, ‘HOST is required’);
module.exports = {
port: PORT,
host: HOST,
url: HOST_URL,
firebaseConfig : {
apiKey: API_KEY,
authDomain: AUTH_DOMAIN,
projectId: PROJECT_ID,
storageBucket: STORAGE_BUCKET,
messagingSenderId: MESSAGING_SENDER_ID,
appId: APP_ID,
},
};
‘use strict’;
const express = require(‘express’);
const cors = require(‘cors’);
const bodyParser = require(‘body-parser’);
const config = require(‘./config’);
const personRoutes = require('./routes/personRoutes');
const app = express();
app.use(express.json());
app.use(cors());
app.use(bodyParser.json());
app.use('/api',personRoutes.routes);
app.listen(config.port, () => console.log(`App is listening on http://localhost:${config.port}`));
const firebase = require(‘firebase’);
const config = require(‘./config’);
const db = firebase.initializeApp(config.firebaseConfig);
module.exports = db;
class Person {
constructor(id, firstName, lastName, nationality){
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.nationality = nationality;
}
}
module.exports = Person;
‘use strict’;
const firebase = require(‘../db’);
const Person = require(‘../models/person’);
const firestore = firebase.firestore();
const addPerson = async (req, res, next) => {
try {
const data = req.body;
await firestore.collection(‘persons’).doc().set(data);
res.send(‘Record saved successfully’);
} catch (error) {
res.status(400).send(error.message);
}
};
const getAllPeople = async (req, res, next) => {
try {
const persons = await firestore.collection(‘persons’);
const data = await persons.get();
const peopleArray = [];
if (data.empty) {
res.status(400).send(‘No person record found’);
} else {
data.forEach(doc => {
const person = new Person(
doc.id,
doc.data().firstName,
doc.data().lastName,
doc.data().nationality,
)
peopleArray.push(person);
});
res.send(peopleArray);
}
} catch (error) {
res.status(400).send(error.message);
}
};
const getPersonById = async (req, res, next) => {
try {
const id = req.params.id;
const person = await firestore.collection(‘persons’).doc(id);
const data = await person.get();
if (!data.exists) {
res.status(400).send(`Student with ${id} not found`);
} else {
res.send(data.data());
}
} catch (error) {
res.status(400).send(error.message);
}
}
const updatePerson = async (req, res, next) => {
try {
const id = req.params.id;
const data = req.body;
const person = await firestore.collection(‘persons’).doc(id);
await person.update(data);
res.send(‘Person updated successfully’)
} catch (error) {
res.status(400).send(error.message);
}
}
const deletePerson = async (req, res, next) => {
try {
const id = req.params.id;
const person = await firestore.collection(‘persons’).doc(id).delete();
res.send(‘Person removed successfully’);
} catch (error) {
res.status(400).send(error.message);
}
}
module.exports = {
addPerson,
getAllPeople,
getPersonById,
updatePerson,
deletePerson
}
const express = require(‘express’);
const { addPerson, getAllPeople, getPersonById, updatePerson, deletePerson } = require(‘../controllers/personController’);
const router = express.Router();router.post(‘/person’, addPerson);
router.get(‘/people’, getAllPeople);
router.get(‘/person/:id’, getPersonById);
router.put(‘/person/:id’, updatePerson);
router.delete(‘/person/:id’, deletePerson);
module.exports = {
routes: router
};

The next step is to create our mobile app

  • Obviously a cross platform code.
  • Very fast development with hot reloads and painting of the page in milliseconds.
  • Seamless integration of the flutter app with Firebase SDK (both android and ios).
  • Native performance.
flutter create mobile_end
firebase_core: ^0.5.3
cloud_firestore: ^0.14.4
class User {
String firstName;
String lastName;
String id;
String nationality;
User({this.firstName, this.lastName, this.id, this.nationality});
factory User.fromJson(Map<dynamic, dynamic> json) {
return User(
firstName: json['firstName'],
lastName: json['lastName'],
id: json['id'],
nationality: json['nationality']);
}
}
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:mobileEnd/User.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: 'Persons',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Mobile app for preview'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<User> data = [];
@override
void initState() {
super.initState();
initializeAndFetch();
}
initializeAndFetch() async {
await Firebase.initializeApp();
fetchData();
}
fetchData() {
CollectionReference collectionReference =
FirebaseFirestore.instance.collection('persons');
collectionReference.snapshots().listen((snapshot) {
final List<User> list = [];
snapshot.docs.forEach((element) {
list.add(User.fromJson(element.data()));
});
setState(() {
data = list;
});
});
}
List<Widget> _renderGrid(BuildContext context, List<User> data) {
final List<Widget> tiles = [];
data.forEach((item) {
tiles.add(Container(
height: 50,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(width: 1.3, color: Color(0xff1f40e6)),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
child: RichText(
text: new TextSpan(
children: [
new TextSpan(
text: item.firstName + ' ',
style: TextStyle(
fontSize: 16,
color: Color(0xff7f7f7f),
fontWeight: FontWeight.w700,
fontStyle: FontStyle.normal,
letterSpacing: 0,
)),
new TextSpan(
text: item.lastName,
style: TextStyle(
color: Color(0xff7f7f7f),
fontSize: 16,
fontWeight: FontWeight.w700,
fontStyle: FontStyle.normal,
letterSpacing: 0,
),
),
],
),
),
),
]),
));
});
return tiles;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Container(
height: 600,
margin: EdgeInsets.only(right: 28, left: 28),
child: GridView.count(
physics: AlwaysScrollableScrollPhysics(),
crossAxisCount: 1,
primary: false,
padding: EdgeInsets.all(0),
children: _renderGrid(context, data),
),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Color(0xff1f000000),
blurRadius: 4,
offset: Offset(0, 2),
)
],
),
),
),
);
}
}

The next step is to create an account on appetize.io

  • Different mobile platforms and devices
  • Cross document messaging
  • App permissions
  • Custom launch pages
  • Playback events of the emulator
flutter build apk --debug

The next step is to create a web front end using ReactJS

npx create-react-app webEnd
cd webEnd
npm install axios
import { webApiGet, webApiPost,webApiPut, webApiDelete } from './methods';const baseUrl = 'http://localhost:8080/api/';export const fetchUsers = () => {
const url = `${baseUrl}people/`;
return webApiGet(url).request;
};
export const createUser = payload => {
const url = `${baseUrl}person/`;
return webApiPost(url, payload).request;
};
export const updateUser = (payload, id) => {
const url = `${baseUrl}person/${id}`;
return webApiPut(url, payload).request;
};
export const deleteUser = id => {
const url = `${baseUrl}person/${id}`;
return webApiDelete(url).request;
};
export const fetchUserById = id => {
const url = `${baseUrl}person/${id}`;
return webApiGet(url).request;
};
import axios from 'axios';function getConfig() {
const config = {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'no-store',
},
};
return { config };
}
export function webApiGet(url) {
const config = getConfig();
return {
request: axios.get(url, config.config),
};
}
export function webApiPut(url, data) {
const config = getConfig();
return {
request: axios.put(url, data, config.config),
};
}
export function webApiPost(url, data) {
const config = getConfig();
return {
request: axios.post(url, data, config.config),
};
}
export function webApiDelete(url, data) {
const config = getConfig();
return {
request: axios.delete(url, data, config.config),
};
}
import React, { useEffect, useState } from 'react';
import './App.css';
import { createUser, deleteUser, fetchUsers, updateUser } from './userOps';
const url = 'https://appetize.io/embed/1fchugudzjegpvqkeck0nke73g?device=iphone8&scale=100&centered=true&autoplay=false&orientation=portrait&deviceColor=black&xdocMsg=true';function App() {
const [users, setUsers] = useState([]);
const [userObj, setUserObj] = useState({
firstName: '',
lastName: '',
nationality: ''
});
useEffect(() => {
fetchUsersMethod();
}, []);
const fetchUsersMethod = async () => {
const users = await fetchUsers();
setUsers(users.data);
}
const handleDelete = async event => {
const { currentTarget: { dataset: { id } } } = event;
await deleteUser(id);
const users = await fetchUsers();
setUsers(users.data);
}
const handleFirstNameChange = async (event, user) => {
const value = event.target.value;
const userIndex = users.findIndex(item => item.id === user.id);
const usersCopy = [...users];
usersCopy[userIndex].firstName = value;
setUsers(usersCopy);
const payload = {
firstName: value,
id: user.id,
lastName: user.lastName,
nationality: user.nationality,
};
await updateUser(payload, user.id);
}
const handleLastNameChange = async (event, user) => {
const value = event.target.value;
const userIndex = users.findIndex(item => item.id === user.id);
const usersCopy = [...users];
usersCopy[userIndex].lastName = value;
setUsers(usersCopy);
const payload = {
firstName: user.firstName,
id: user.id,
lastName: value,
nationality: user.nationality,
};
await updateUser(payload, user.id);
}
const handleNationalityChange = async (event, user) => {
const value = event.target.value;
const userIndex = users.findIndex(item => item.id === user.id);
const usersCopy = [...users];
usersCopy[userIndex].nationality = value;
setUsers(usersCopy);
const payload = {
firstName: user.findIndex,
id: user.id,
lastName: user.lastName,
nationality: value,
};
await updateUser(payload, user.id);
}
const setUser = (e, field) => {
const value = e.target.value;
const user = { ...userObj };
switch (field) {
case 'firstName': {
user.firstName = value;
setUserObj(user);
break;
}
case 'lastName': {
user.lastName = value;
setUserObj(user);
break;
}
case 'nationality': {
user.nationality = value;
setUserObj(user);
break;
}
default: break;
}
}
const addUser = async () => {
await createUser(userObj);
const users = await fetchUsers();
setUsers(users.data);
}
return (
<div className="App">
<div className="left_container">
<div className="user_table">
{users.map((user, index) => (
<div className="user_row" key={index}>
<div className="user_details">
<div className="field"><input value={user.firstName} type="text" onChange={(e) => handleFirstNameChange(e, user)} /></div>
<div className="field"><input value={user.lastName} type="text" onChange={(e) => handleLastNameChange(e, user)} /></div>
<div className="field"><input value={user.nationality} type="text" onChange={(e) => handleNationalityChange(e, user)} /></div>
</div>
<div className="user_ops">
<button className="delete" data-id={user.id} onClick={handleDelete}>DELETE</button>
</div>
</div>
))}
</div>
<div className="add_user">
<div className="add_user_row">
<span>First name: </span>
<input type="text" onChange={(e) => setUser(e, 'firstName')} />
</div>
<div className="add_user_row">
<span>Last name: </span>
<input type="text" onChange={(e) => setUser(e, 'lastName')} />
</div>
<div className="add_user_row">
<span>Nationality: </span>
<input type="text" onChange={(e) => setUser(e, 'nationality')} />
</div>
<button onClick={addUser}>Add user</button>
</div>
</div>
<div className="iframe_container" >
<iframe title="appetize_-io" src={url} width="100%" height="100%" frameBorder="0"></iframe>
</div>
</div>
);
}
export default App;
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
.App {
display: flex;
justify-content: space-between;
align-items: center;
}
.iframe_container {
width: 40%;
height: 100%;
}
.left_container {
width: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.left_container .user_table {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: auto;
overflow: scroll;
border: 1px solid black;
padding: 20px;
}
.left_container .user_table .user_row {
margin: 20px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 16px;
width: 100%;
}
.left_container .user_table .user_row .user_details {
display: flex;
justify-content: space-between;
align-items: center;
width: 70%;
}
.left_container .user_table .user_row .user_details .field {
width: 33%;
}
.left_container .add_user {
display: flex;
flex-direction: column;
margin-top: 40px;
}
.left_container .add_user button {
margin-top: 10px;
}

--

--

--

Information geek, Seeker

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Testing React with Jest, Enzyme and Typescript (Part 3): Shallow

Using custom CSS Variables

JavaScript Problems — Strings, Arrays and More

Building Your First Dockerized MERN Stack Web App

Learn how React Context API works by Building a Minimal Ecommerce Shopping App

Finding the most and least frequent elements in JavaScript

Technologies behind Progressive Web App

A Beginner’s quick setup of a basic JavaScript project, and linking a Jest Test file.

Package.json file where we have updated Jest:test

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Aditya Chauhan

Aditya Chauhan

Information geek, Seeker

More from Medium

Exploring Flutter as a React Developer

Flutter

Why we chose Flutter for Mindfully

Coding an Instagram Clone With Flutter and Feeds

Flutter Hooks — A new beginning