Flutter: How to do CRUD with PostgreSQL? Part 1


I am going to share today some exciting topics such as :

We are going to build both of these using only Dart. It is a multi-purpose language developed by Google for building web, mobile and server applications.

What is Aqueduct?

Aqueduct is an HTTP web server framework used for building REST applications using Dart. REST short for Representational State Transfer api allows client-server interaction using HTTP protocol. Each url is called request while data sent back is call response. The four methods of request we are going to cover today is GET, POST, PUT and DELETE. In HTTP, resource means data which is the one being transferred and manipulated.

  • GET — Get a list of resources or single resource
  • POST — Create new resource
  • PUT — Update existing resource
  • DELETE — Delete a resource

At the point of this writing, Aqueduct has just released version 3.0.0. Refer here for the change log.

How to setup Aqueduct

Installing Aqueduct

pub global activate aqueduct

Create new Aqueduct project

aqueduct create heroes

Start Aqueduct server

aqueduct serve

Enter the following at your browser (I am using port 8888. This port maybe different from yours and is visible at your terminal after starting aqueduct server)

http://localhost:8888/example

You should see the following on your browser

{"key":"value"}

I am basically going through what has been described in detail in Aqueduct’s official tutorial here. So do checkout the link for more information.

In the channel.dart, our router is defined inside entryPointer. This makes our router the first to receive and checks all HTTP request before passing into any controller which handles the request.

Let’s create a folder called controller to contain all controller dart files. In this folder, we will add heroes_controller.dart. Add the following code to your new file.

import 'package:aqueduct/aqueduct.dart';
import 'package:heroes/heroes.dart';

class HeroesController extends Controller {
final _heroes = [
{'id': 11, 'name': 'Captain America'},
{'id': 12, 'name': 'Ironman'},
{'id': 13, 'name': 'Wonder Woman'},
{'id': 14, 'name': 'Hulk'},
{'id': 15, 'name': 'Black Widow'},
];

@override
Future<RequestOrResponse> handle(Request request) async {
return Response.ok(_heroes);
}
}

Now we have Heroes Controller which is a subclass of Controllers and returns list of heroes stored in _heroes. Let’s head back to our channel.dart and import this file.

import 'controller/heroes_controller.dart';

Next, let’s add a new path /heroes in the router. So whenever application receives request with path heroes it will pass it to our HeroesController.

router
 .route('/heroes')
 .link(() => HeroesController());

Let’s stop our application using Ctrl+ c and restart by running aqueduct serve again. Then let’s enter the link below in our browser.

http://localhost:8888/heroes

You should get JSON value in the browser.

[{"id":11,"name":"Captain America"},{"id":12,"name":"Ironman"},{"id":13,"name":"Wonder Woman"},{"id":14,"name":"Hulk"},{"id":15,"name":"Black Widow"}]

If you encounter error of HeroesController not able to be recognised, try saving all your opened dart files and rerun your application. In mac, press ⌥ + ⌘ + s

Now we would like to send a request to our application to retrieve a single hero. Let’s modify the /heroes route in channel.dart.

router
.route('/heroes/[:id]')
.link(() => HeroesController());

Anything within the square bracket is optional. We will also modify our HeroesController to be able to handle single hero request.

@override
Future<RequestOrResponse> handle(Request request) async {
if (request.path.variables.containsKey('id')) {
final id = int.parse(request.path.variables['id']);
final hero = _heroes.firstWhere((hero) => hero['id'] == id, orElse: () => null);
if (hero == null) {
return Response.notFound();
}

return Response.ok(hero);
}

return Response.ok(_heroes);
}

Let’s restart our application and make single hero request.

Note: You can also use the terminal to do request, for example: curl -X GET http://localhost:8888/heroes/11

{"id":11,"name":"Captain America"}

Notice till this point, we are only returning a list of heroes or single hero. To handle more complicate HTTP request such as delete, get, post or put, we need to use ResourceController. Let’s replace our heroes_controller.dart content to the following code

import 'package:aqueduct/aqueduct.dart';
import 'package:heroes/heroes.dart';
class HeroesController extends ResourceController {
final _heroes = [
{'id': 11, 'name': 'Captain America'},
{'id': 12, 'name': 'Ironman'},
{'id': 13, 'name': 'Wonder Woman'},
{'id': 14, 'name': 'Hulk'},
{'id': 15, 'name': 'Black Widow'},
];
@Operation.get()
Future<Response> getAllHeroes() async {
return Response.ok(_heroes);
}
@Operation.get('id')
Future<Response> getHeroByID(@Bind.path('id') int id) async {
final hero = _heroes.firstWhere((hero) => hero['id'] == id, orElse: () => null);
if (hero == null) {
return Response.notFound();
}
return Response.ok(hero);
}
}

Notice we are using @Bind.path('id') int id as the input parameter. This is to declare our input parameter which is path('id') and bind it as argument of type integer for the method getHeroByID.

Up to this point, we are hard coding the arrays of heroes into our code. What we like to do next is to store data in SQL database and allowing application to do CRUD functions.

How to setup PostgreSQL

Remove previous versions of PostgreSQL

brew uninstall --force postgresql

Delete all files of PostgreSQL

rm -rf /usr/local/var/postgres

Install Postgresql using homebrew

brew install postgres

Start postgres server

pg_ctl -D /usr/local/var/postgres start

If you have trouble starting postgres server, chances are you might have it already running in background. Use the following command to end all processes.

sudo pkill -u postgres

Access psql prompt

psql postgres 

Exit psql prompt

postgres=# q

Switching database (by default is postgresql)

postgres-# c heroes

List all tables

postgres-# dt

List content of a single table

heroes=# SELECT * FROM _hero;

Let’s setup our database. Type the following at the psql prompt.

CREATE DATABASE heroes;
CREATE USER heroes_user WITH createdb;
ALTER USER heroes_user WITH password 'password';
GRANT all ON database heroes TO heroes_user;

Create migration file

Run the following command to create migration file call as /heroes/migrations/00000001_initial.migration.dart

aqueduct db generate

Edit migration file

Inside the migration file, we need to define the upgrade method

Future upgrade() async {
database.createTable(SchemaTable(
"_Hero", [
SchemaColumn("id", ManagedPropertyType.bigInteger,
isPrimaryKey: true, autoincrement: true, isIndexed: false, isNullable: false, isUnique: false),
SchemaColumn("name", ManagedPropertyType.string,
isPrimaryKey: false, autoincrement: false, isIndexed: false, isNullable: false, isUnique: true),
],
));
}

Also we need to add seed method to add some values to our table

@override
Future seed() async {
final heroNames = ["Mr. Nice", "Narco", "Bombasto", "Celeritas", "Magneta"];

for (final heroName in heroNames) {
await database.store.execute("INSERT INTO _Hero (name) VALUES (@name)", substitutionValues: {
"name": heroName
});
}
}

Apply migration file

Run the following command to apply the migration file.

aqueduct db upgrade --connect postgres://heroes_user:password@localhost:5432/heroes

Below is output of the terminal after running the command.

DAVIDs-MBP:heroes davidcheah$ aqueduct db upgrade --connect postgres://heroes_user:password@localhost:5432/heroes
-- Aqueduct CLI Version: 3.0.0
-- Aqueduct project version: 3.0.0
2018-08-26 21:39:23.845 +08 [70735] ERROR:  relation "_aqueduct_version_pgsql" does not exist at character 42
2018-08-26 21:39:23.845 +08 [70735] STATEMENT:  SELECT versionNumber, dateOfUpgrade FROM _aqueduct_version_pgsql ORDER BY dateOfUpgrade ASC
-- Updating to version 1 on new database...
PostgreSQL connecting, heroes_user@localhost:5432/heroes.
Initializating database...
CREATE TABLE _aqueduct_version_pgsql (versionNumber INT NOT NULL UNIQUE,dateOfUpgrade TIMESTAMP NOT NULL)
Applying migration version 1...
CREATE TABLE _Hero (id BIGSERIAL PRIMARY KEY,name TEXT NOT NULL UNIQUE)
Seeding data from migration version 1...
Query:execute (10ms) INSERT INTO _Hero (name) VALUES (@name) -> []
Query:execute (0ms) INSERT INTO _Hero (name) VALUES (@name) -> []
Query:execute (0ms) INSERT INTO _Hero (name) VALUES (@name) -> []
Query:execute (0ms) INSERT INTO _Hero (name) VALUES (@name) -> []
Query:execute (0ms) INSERT INTO _Hero (name) VALUES (@name) -> []
Applied schema version 1 successfully.

Restart your application with aqueduct server. Refresh your browser again and you should see the same list of heroes except this time it is from the Postgresql database. You can also use this link http://aqueduct-tutorial.stablekernel.io/#/dashboard to test your application.

Let’s further improve our application by adding method for POST, PUT and DELETE.

Post

@Operation.post()
Future<Response> createHero(@Bind.body() Hero inputHero) async {
final query = Query<Hero>(context)
..values = inputHero;
final insertedHero = await query.insert();
return Response.ok(insertedHero);
}

Put

@Operation.put('id')
Future<Response> updateHeroById(@Bind.path('id') int id ,@Bind.body() Hero inputHero) async {
final heroQuery = Query<Hero>(context)
..where((h) => h.id).equalTo(id)
..values = inputHero;
final hero = await heroQuery.updateOne();
if (hero == null) {
return Response.notFound();
}
return Response.ok(hero);
}

Delete

@Operation.delete('id')
Future<Response> deleteHeroByID(@Bind.path('id') int id) async {
final heroQuery = Query<Hero>(context)..where((h) => h.id).equalTo(id);
final hero = await heroQuery.delete();
if (hero == null) {
return Response.notFound();
}
return Response.ok(hero);
}

Use this link http://aqueduct-tutorial.stablekernel.io/#/dashboard to test your RESTful apis.


Our application with RESTful api is finally implemented and tested with web client. In the next post, we are going to see how to build flutter app to do basic create, read, update and delete with our server application.

GitHub

https://github.com/tattwei46/flutter_crud_postgresql

Reference

Aqueduct IO

https://aqueduct.io/docs/

PSQL

https://www.codementor.io/engineerapart/getting-started-with-postgresql-on-mac-osx-are8jcopb

PSQL cheat sheet

https://gist.github.com/Kartones/dd3ff5ec5ea238d4c546

PSQL commands

https://www.codementor.io/engineerapart/getting-started-with-postgresql-on-mac-osx-are8jcopb


The Flutter Pub is a medium publication to bring you the latest and amazing resources such as articles, videos, codes, podcasts etc. about this great technology to teach you how to build beautiful apps with it. You can find us on Facebook, Twitter, and Medium or learn more about us here. We’d love to connect! And if you are a writer interested in writing for us, then you can do so through these guidelines.

Flutter : How to do user login with Firebase


Release

25/12/18 — Updated latest code snippet after refactoring and clean up.

24/01/19 — Duplicated link to github at the top of article.

Source Code

In case you want to skip the whole mumbo jumbo, you can grab the source code here 👇

https://github.com/tattwei46/flutter_login_demo

Update

Here is a sequel to this post which is How to do CRUD with Firebase RTDB. Check it out!

What is Flutter?

Flutter is an open source mobile SDK developed by Google to build high quality applications for Android and iOS. It allows developers to not only build application with beautiful design, smooth animation and fast performance, but also able to integrate new features quickly. Flutter offers high velocity development with its stateful hot reload and hot restart. With only one code base to manage, you get to save a lot of cost comparing to managing both Android and iOS projects as Flutter compiles it to native ARM code. Flutter uses Dart programming language which is also developed by Google.

Why Dart?

  • A terse, strongly-typed, object-oriented language.
  • Supports just-in-time and ahead-of-time compilation.
  • JIT allows flutter to recompile code directly on the device while the app is still running.
  • Enable fast development and enables sub-second stable hot reloading.
  • AOT allows code to be compiled directly into native ARM code leading to fast startup and predictable performance.

What is Firebase

Firebase is a mobile and web development platform that provides developer with wide range of products. Today we will be looking how to build our first flutter application with Firebase authentication and realtime database. This application allows user to signup or login and perform todo items CRUD with Firebase. On this post, we are going to solely focus on the user signup and login part.

How to setup environment

  • Follow the instructions in the this link
  • Get the Flutter SDK
  • Run Flutter doctor to install any dependencies
flutter doctor
  • Use the command below to open iOS simulator
open -a Simulator
  • To open android emulator, launch Android Studio > tools > AVD Manager and select create Virtual Device.

Building Flutter App

You can get the complete source code in the GitHub link at the bottom of the post. The following shows how do we derive from Flutter sample project to complete source code in GitHub.

👉Step 1: Create a new flutter project call flutter login demo. Launch simulator and run project using flutter. You can use either Android Studio or VSCode as your preferred IDE. Steps to configure your editor in here.

flutter run

If you have both Android emulator and iOS Simulator running, run the following command to execute on both.

flutter run -d all

You should see similar screens on both Android Emulator and iOS Simulator.


Left: Android, Right: iOS

If you’re interested to know how to get screenshots at your simulators;

For Android: Simply click the camera icon on the left side of the tool pane. The image will be saved to desktop

For iOS: Hold and press command + shift + 4. Press the space bar to change Mouse pointer to camera icon. Point to iOS simulator, click to take screenshot. The image will be saved to desktop.

👉Step 2: At main.dart, erase all contents and add the following boilerplate to your file. We are going to create a new file called login_page.dart which has LoginPage class. On your terminal, hit the R key to perform hot reload and you should see “Hello World” on the screen.

Main.dart

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

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

class MyApp extends StatelessWidget {

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

login_signup_page.dart

import 'package:flutter/material.dart';

class LoginSignUpPage extends StatelessWidget {

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Flutter login demo"),
),
body: new Container(
child: new Text("Hello World"),
),
);
}
}


👉Step 3: Changing from stateless to stateful.

login_signup_page.dart

import 'package:flutter/material.dart';

class LoginSignUpPage extends StatefulWidget {

@override
State<StatefulWidget> createState() => new _LoginSignUpPageState();

}

class _LoginSignUpPageState extends State<LoginSignUpPage>{

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Flutter login demo"),
),
body: new Container(
child: new Text("Hello World"),
),
);
}
}

👉Step 4: Inside the Scaffold body, let’s replace the Hello Word text to a Form and inside it we will put a ListView. A ListView takes an array of widgets. We will refactor out each UI component into separate widget.

Whenever we are using text input, it is better to wrap it around a ListView to prevent rendering error when the soft keyboard shows up due to overflow pixels.

login_signup_page.dart

@override
Widget build(BuildContext context) {
_isIos = Theme.of(context).platform == TargetPlatform.iOS;
return new Scaffold(
appBar: new AppBar(
title: new Text('Flutter login demo'),
),
body: Stack(
children: <Widget>[
_showBody(),
_showCircularProgress(),
],
));
}

👉Step 5: Building each UI components

Notice in the Scaffold body, we have a Stack widget as the body. Basically what I want to do is to show user a circular loading indicator, when any login or sign up activity is running. To do this, we need to overlay a CircularProgressIndicator (thankfully, Flutter already has this widget, so to use it, just simply call it) with our main widget layout (the login/signup form). That is the function of the Stack widget, which allows one widget to overlay on top of another widget. To control whether to show the CircularProgressIndicator or not, we check on bool _isLoading whether now the screen is loading or not.

Widget _showCircularProgress(){
if (_isLoading) {
return Center(child: CircularProgressIndicator());
} return Container(height: 0.0, width: 0.0,);

}

For the logo, we will use a hero widget and also to import the image by adding the following line in your pubspec.yaml. Then run get packages to import your image.

assets:
- assets/flutter-icon.png

login_signup_page.dart

Widget _showLogo() {
return new Hero(
tag: 'hero',
child: Padding(
padding: EdgeInsets.fromLTRB(0.0, 70.0, 0.0, 0.0),
child: CircleAvatar(
backgroundColor: Colors.transparent,
radius: 48.0,
child: Image.asset('assets/flutter-icon.png'),
),
),
);
}

[Update] Previously we use flexible spacing widget using SizedBox that takes in height input to have a vertical spacing between the 2 widgets. Now we just place one widget inside a padding widget and use padding: EdgeInsets.fromLTRB() which means from left, top, right and bottom and enter the value of padding at correct position accordingly.

Next comes our email and password text form field. Notice for each field we have validator and onSaved. These 2 callbacks will be triggered when form.validate() and form.save() is called. So for example if form.save() is called, the value in the text form field is copied into another local variable.

We will also introduce a validator to our fields to check if field input is empty and then show warning to user in red. We also need to create variables _email and _password to store the values. For password, we set obsecureText: true to hide user password.

Widget _showEmailInput() {
return Padding(
padding: const EdgeInsets.fromLTRB(0.0, 100.0, 0.0, 0.0),
child: new TextFormField(
maxLines: 1,
keyboardType: TextInputType.emailAddress,
autofocus: false,
decoration: new InputDecoration(
hintText: 'Email',
icon: new Icon(
Icons.mail,
color: Colors.grey,
)),
validator: (value) => value.isEmpty ? 'Email can't be empty' : null,
onSaved: (value) => _email = value,
),
);
}

Widget _showPasswordInput() {
return Padding(
padding: const EdgeInsets.fromLTRB(0.0, 15.0, 0.0, 0.0),
child: new TextFormField(
maxLines: 1,
obscureText: true,
autofocus: false,
decoration: new InputDecoration(
hintText: 'Password',
icon: new Icon(
Icons.lock,
color: Colors.grey,
)),
validator: (value) => value.isEmpty ? 'Password can't be empty' : null,
onSaved: (value) => _password = value,
),
);
}

Next we need to add the primary button but it should be able to display the correct text depending on whether the user wants to sign up for new account or login with existing account. For this, we need to create an enum to keep track whether the form is for login or signup.

enum FormMode { LOGIN, SIGNUP }

We will assign a method for the button callback function. For this, we will create method called _validateAndSubmit that will pass in both email and password for Firebase authentication. More on that later in this post.

Widget _showPrimaryButton() {
return new Padding(
padding: EdgeInsets.fromLTRB(0.0, 45.0, 0.0, 0.0),
child: new MaterialButton(
elevation: 5.0,
minWidth: 200.0,
height: 42.0,
color: Colors.blue,
child: _formMode == FormMode.LOGIN
? new Text('Login',
style: new TextStyle(fontSize: 20.0, color: Colors.white))
: new Text('Create account',
style: new TextStyle(fontSize: 20.0, color: Colors.white)),
onPressed: _validateAndSubmit,
));
}

Now we need to add a secondary button for user to be able to toggle between signup and login form. On the onPressed method, we would like to toggle the state of the form between LOGIN and SIGNUP. Notice for secondary button, we are using FlatButton instead of RaisedButton like the previous submit button. The reason is if you have 2 buttons and would like to make 1 more distinctive than the other, RaisedButton is the right choice as it instantly catches the users’ attention compare to FlatButton.

login_page.dart

Widget _showSecondaryButton() {
return new FlatButton(
child: _formMode == FormMode.LOGIN
? new Text('Create an account',
style: new TextStyle(fontSize: 18.0, fontWeight: FontWeight.w300))
: new Text('Have an account? Sign in',
style:
new TextStyle(fontSize: 18.0, fontWeight: FontWeight.w300)),
onPressed: _formMode == FormMode.LOGIN
? _changeFormToSignUp
: _changeFormToLogin,
);
}

On the method to toggle form mode, it is crucial to wrap it around setState as we need to tell Flutter to re-render the screen with updated value of the FormMode.

void _changeFormToSignUp() {
_formKey.currentState.reset();
_errorMessage = "";
setState(() {
_formMode = FormMode.SIGNUP;
});
}

void _changeFormToLogin() {
_formKey.currentState.reset();
_errorMessage = "";
setState(() {
_formMode = FormMode.LOGIN;
});
}

Next, we are going to have a _showErrorMessage() that pass the error message to user from Firebase side when they are trying to do login or signup. This error message could be something like “There is already an existing user account”. So we are going to have a String _errorMessage to store the error message from Firebase.

Widget _showErrorMessage() {
if (_errorMessage.length > 0 && _errorMessage != null) {
return new Text(
_errorMessage,
style: TextStyle(
fontSize: 13.0,
color: Colors.red,
height: 1.0,
fontWeight: FontWeight.w300),
);
} else {
return new Container(
height: 0.0,
);
}
}

Finally, let’s arrange those individual UI components and put it back to our ListView.

Widget _showBody(){
return new Container(
padding: EdgeInsets.all(16.0),
child: new Form(
key: _formKey,
child: new ListView(
shrinkWrap: true,
children: <Widget>[
_showLogo(),
_showEmailInput(),
_showPasswordInput(),
_showPrimaryButton(),
_showSecondaryButton(),
_showErrorMessage(),
],
),
));
}

TextFormField validator in action

👉Step 6: Register new project with Firebase

Go to https://console.firebase.google.com and register new project.

For android, click the android icon. Enter your package name which can be found in android/app/src/main/AndroidManifest.xml

Download the config file which is google-services.json (Android).

Drag the google-services.json into app folder in project view


We need to add the Google Services Gradle plugin to read google-services.json. In the /android/app/build.gradle add the following to the last line of the file.

apply plugin: 'com.google.gms.google-services'

In android/build.gradle, inside the buildscript tag, add new dependency.

buildscript {
   repositories {
      //...
}
dependencies {
   //...
   classpath 'com.google.gms:google-services:3.2.1'
}

For iOS, open ios/Runner.xcworkspace to launch Xcode. The package name can be found in bundle identifier at Runner view.

Download the config file which is GoogleService-info.plist (iOS).

Drag the GoogleService-info.plist into the Runner subfolder inside Runner as shown below.


👉Step 7: Add dependencies in pubspec.yaml
Next we need to add firebase_auth dependency in pubspec.yaml. To get the latest version number, go to https://pub.dartlang.org/ and search for firebase auth.

firebase_auth: ^0.6.6

👉Step 8: Import Firebase Auth

import 'package:firebase_auth/firebase_auth.dart';

👉Step 9: Enable sign up using email and password at Firebase


👉Step 10: Sign in to Firebase

Firebase signInWithEmailAndPassword is a method that returns a future value. Hence the method needs to have await and the external wrapper function needs to have async. So we enclose the login and signup methods with try catch block. If there was an error, our catch block should be able to capture the error message and display to the user.

There is a difference in how the actual message is store in the error thrown by Firebase. In IOS, the message is in e.details whereas for Android, is in e.message. You can easily check the platform by using _isIos = Theme.of(context).platform == TargetPlatform.iOS and it should be inside any of the build widget method because it needs a context.

_validateAndSubmit() async {
setState(() {
_errorMessage = "";
_isLoading = true;
});
if (_validateAndSave()) {
String userId = "";
try {
if (_formMode == FormMode.LOGIN) {
userId = await widget.auth.signIn(_email, _password);
print('Signed in: $userId');
} else {
userId = await widget.auth.signUp(_email, _password);
print('Signed up user: $userId');
}
if (userId.length > 0 && userId != null) {
widget.onSignedIn();
}
} catch (e) {
print('Error: $e');
setState(() {
_isLoading = false;
if (_isIos) {
_errorMessage = e.details;
} else
_errorMessage = e.message;
});
}
}
}

👉Step 11: Clear form field when toggle

We need to add the following line in both _changeFormToSignUp and _changeFormToLogin to reset form field everytime user switches between login and sign up form.

formKey.currentState.reset();

👉Step 12: Try to sign up a user

Let’s try to sign up a user by entering an email and password.

If you encounter something like below, this is because there is an extra spacing at the end of your email

I/flutter (14294): Error PlatformException(exception, The email address is badly formatted., null)

If you encounter something like below, change your password to be at least 6 characters long.

I/flutter (14294): Error PlatformException(exception, The given password is invalid. [ Password should be at least 6 characters ], null)

Finally once success, you should be able to see in your terminal the following line. The random string is the user ID.

I/flutter (14294): Signed up JSwpKsCFxPZHEqeuIO4axCsmWuP2

Similarly if we try to sign in the same user we signed up, we should get something like this:

I/flutter (14294): Signed in JSwpKsCFxPZHEqeuIO4axCsmWuP2

👉Step 13: Implement Auth class

Create new file call authentication.dart. We are also going to implement abstract class BaseAuth. Purpose of this abstract class is it acts as a middle layer between our UI components and the actual implementation class which depends on the framework we choose. In any case, we decided to swap Firebase to something like PostgreSQL, then it would not have impact on the UI components.

import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';

abstract class BaseAuth {
Future<String> signIn(String email, String password);
Future<String> signUp(String email, String password);
Future<String> getCurrentUser();
Future<void> signOut();
}

class Auth implements BaseAuth {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;

Future<String> signIn(String email, String password) async {
FirebaseUser user = await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
return user.uid;
}

Future<String> signUp(String email, String password) async {
FirebaseUser user = await _firebaseAuth.createUserWithEmailAndPassword(email: email, password: password);
return user.uid;
}

Future<String> getCurrentUser() async {
FirebaseUser user = await _firebaseAuth.currentUser();
return user.uid;
}

Future<void> signOut() async {
return _firebaseAuth.signOut();
}
}

In login_page.dart

class LoginSignUpPage extends StatefulWidget {
LoginSignUpPage({this.auth});
final BaseAuth auth;
@override
State<StatefulWidget> createState() => new _LoginPageState();
}

In main.dart

home: new LoginSignUpPage(auth: new Auth())

Back in login_page.dart, lets swap our signInWithEmailAndPassword

String userId = await widget.auth.signIn(_email, _password);
String userId= await widget.auth.signUp(_email, _password);

👉Step 14: Root and home with VoidCallback

Let’s create a new file call home_page.dart. This will display empty to do list after user successfully signed in or registered. As usual, we implement Scaffold with AppBar but this time we are going to have a FlatButton inside AppBar for logout function. This logout calls the Firebase signout method inside BaseAuth class.

We also need to create a file call root_page.dart. This will be replacing home: LoginSignUpPage(auth: new Auth()) in our main.dart.

home: new RootPage(auth: new Auth())

When the app launches, it should navigate to this page. This page acts as a manager to check for valid Firebase user id and directs them to appropriate page according. For example if the user id is present, meaning user is already signed in and user should be shown the home_page instead of login_signup_page. This will be done inside initState which is the function that will be executed first in the file.

In the root_page, there will be 2 methods which is the _onLoggedIn and _onSignedOut. In the _onLoggedIn , we try to get the user id and setstate authStatus to user is already logged in. In the _onSignedOut , we clear the stored user id and setstate authStatus to user is not logged in.

In the root_page, we pass in 2 parameters into login_page, one is the Auth class that we implement easier (we instantiate it at main.dart) and _onLoggedIn method). In the login_signup_page, we create 2 variables which is auth of type BaseAuth and onSignedIn of type VoidCallback. We can easily retrieve the 2 parameters passed into login_signup_page into our local variables using the following line.

LoginSignUpPage({this.auth, this.onSignedIn});

final BaseAuth auth;
final VoidCallback onSignedIn;

VoidCallback allows login_signup_page to call the method inside root_page which is the _onSignedIn when the user signs in. When _onSignedIn is called, it will set the authStatus to LOGGED_IN and setState to redraw the app. When the app is redrawn, initState checks the authStatus and since it is LOGGED_IN, it will show home_page, passing in auth and voidcallback of _signOut.

root_page.dart

@override
Widget build(BuildContext context) {
switch (authStatus) {
case AuthStatus.NOT_DETERMINED:
return _buildWaitingScreen();
break;
case AuthStatus.NOT_LOGGED_IN:
return new LoginSignUpPage(
auth: widget.auth,
onSignedIn: _onLoggedIn,
);
break;
case AuthStatus.LOGGED_IN:
if (_userId.length > 0 && _userId != null) {
return new HomePage(
userId: _userId,
auth: widget.auth,
onSignedOut: _onSignedOut,
);
} else return _buildWaitingScreen();
break;
default:
return _buildWaitingScreen();
}
}

Notice the debug ribbon at the top right corner of the app, you can easily remove it by adding the following line inside MaterialApp widget in main.dart

Demo login screen
debugShowCheckedModeBanner: false,

Debug banner removed

You can get the complete source code in the github link below

https://github.com/tattwei46/flutter_login_demo

If you find this article is useful, give some 👏

Reference:

https://github.com/tattwei46/flutter_login_demo


The Flutter Pub is a medium publication to bring you the latest and amazing resources such as articles, videos, codes, podcasts etc. about this great technology to teach you how to build beautiful apps with it. You can find us on Facebook, Twitter, and Medium or learn more about us here. We’d love to connect! And if you are a writer interested in writing for us, then you can do so through these guidelines.


Mallsyok : The Technicals

For this post, I am going to share the technical aspects behind Mallsyok app. I have previously written two posts regarding Mallsyok introduction and workflow with tools. Make sure to read the previous posts by clicking the link below and give some 👏

Series


High-level View

In the images below are the screens available in Mallsyok and how they interact with one another. Firstly, users will need select one from list of malls which will then bring them to a list of outlets based on their mall selection. Once a mall is selected, they can either select an outlet from list of outlets, select map (flow 1) or select other information related to the mall (flow 2).

App Screen Flow 1

App Screen Flow 2

MVC

Mallsyok uses the basic MVC which stands of Model-View-Controller pattern. This helps to isolate the different roles of this application. By default, the tools offered by Android Studio with layouts, activities and data structure steers any application into MVC pattern. Model represents the data layer. Mallsyok uses model to obtain and store information regarding malls and outlets. View is the user interface layer or what the user sees on their screen. Controller is the logic layer which gets notified of users’ behaviour and updates model if needed. This logic layer does all data processing, controls the visibility of certain UI components and responsible for retrieving information from database in background.

Here is how it looks like at high level for our Mallsyok app in MVC.

MVC

Database

Mallsyok stores all mall and outlet information in the database. Back then we got to know Firebase Realtime Database from some video tutorials and decided to try it. NoSQL was relatively new to us in the sense we had to design the simplest and denormalised or flattened data structure. Due to its ease of implementation, we were able to integrate RTDB into our app and got it running in no time.

Firebase Realtime Database stores data in JSON tree structure

Then Firebase Cloud Firestore came swinging by and we were interested as well. Different from RTDB which stores data in form of JSON tree structure, Cloud Firestore stores them in collections of documents. A document stores key-value pairs whereas collection stores a set of documents.

Click on this link to understand the difference in features between RTDB and Firestore.

We migrated to Firestore because we were convinced it was a better option compare to RTDB. However it was few days after our app was officially out in the playstore that we realised that it was a mistake 😰.

This is due to the way Firestore is charging and the way our app works. Firestore charges by the number of reads, writes and deletes. If you go for the free account, there is a limited number of each operation per day. If you go beyond that limit then you won’t be able to perform that specific operation until the next day when the counter resets. Let’s assume a user chooses a mall that contains 1,000 outlets so that would be 1,000 read operations. Due to this, Mallsyok can easily exceed the limited operations in a day. A better option will be RTDB where it charges by amount of data in GB stored and downloaded.

You can understand more on the differences in charging here.

Cloud Firestore stores data in collection of documents

Let’s start to dive into some of the special features and how it was implemented in Mallsyok!

Auto-locate

This feature helps user to skip selection of mall if they are already at that mall. By using combination of network and GPS, depending on which is stronger, Mallsyok is able to obtain location of user’s phone and matches to the nearest mall. Initially LocationManager was used to obtain user location but this depends only on GPS signal. Unfortunately when user is inside the mall, GPS signal will be too weak and in the end the app just shows time out when user requests for auto locate. Luckily we found FusedLocationProviderClient which uses combination of network or GPS to obtain user location. To use this api, firstly we need to declare the permission needed in AndroidManifest.xml

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Next we can build location settings request. In this request we can set specific parameters such as interval of location update and accuracy. More information on the location settings here.


These parameters will be pass to LocationSettingsRequest to be built.


We use checkLocationSettings check if these settings request is satisfactory. We too need to check if user has granted access for the app to use their location. Finally we request for user location either in continuous updates or just last known location using getLastLocation method and return this location on the addOnSuccessListener.


You can test location on android emulator by setting latitude and longitude. To do this, click on the more icon to bring out the extended control window. Click on location and enter both latitude and longitude. The method getLastLocation may return null because the location cache is not updated. Hence launch google map to force updated location cache and getLastLocation should be able to return the location that you entered. Can more information here.

Pretty quick!

Search Function

Mallsyok has a search bar function for users to be able to find malls or outlets quickly. The listview which displays list of malls or outlets will updates itself whenever there is a change in text entered in search bar.

Once we have done searching, we notify our ArrayAdapter

Retrieving the list again and again from Firestore is expensive, hence we store the list as global variable and reload listview when user resets the search bar.

Search bar that works!

Offline State

We added this feature to prevent user from having bad UI experience when they are cut off from internet. Hence we added a check for network availability whenever a new screen is loaded.


If isNetworkAvailable returns a false, we simply load a screen with refresh icon and toast saying no internet detected. When the network is available again, user just tap on the refresh icon and it will reload the screen.

Spinning wheel of refresh

Interactive Static Information

Mallsyok helps to provide information such as contact, address and website. But rather than to display static information, Mallsyok extends to help users interact with the information. Simply tapping those information, users can perform action based on it. For those to happen, we simply pass the string tapped by user into respective helper function to start new a intent.






Further enhance your experience with information

Map View

User can view floor plans in Mallsyok when they tap on Map menu. For this to happen, we store all images in Firebase Storage with floor number as their file name. We need to store information of how many levels a mall has, hence we keep it in the Firebase Cloud Firestore. Hence, when user taps on Map, our app grabs the list of floor levels and from the list, grabs the correct images to be rendered on the screen. For image downloading, we use Picasso which is powerful and easy to implement library.

To start using Picasso, simply add in build.gradle (Module:app)

implementation 'com.squareup.picasso:picasso:2.71828'

getImageURI fetches image from Firebase Storage

To render the image on the screen, we use TouchImageView implementation by MikeOrtiz. His amazing implementation helps you to render image with extensive features such as dragging, pinch zoom, fling, double tap zoom and animation polish. To start using, simply place TouchImageView.java into your project and assign image just like how you do it with ImageView. See the magic below 🤤

Cool huh?

Ending note

Well I guess this post wraps up everything that I need to share about Mallsyok. If you like this post and find it informative, kindly click the 👏 to encourage me to write more 😄👍

Feel free to try out the app itself on Android. Apologies to iphone 📱 users that this app is not available 🙏


Check me out :

Mallsyok : The Workflow

Design done using Sketch

I have written about Mallsyok introduction in previous post.

In this post, I will be sharing our workflow of our team.

Update: Unfortunately, we have stopped building new features and maintaining Mallsyok app. But that will not stop me from sharing our experience out 😜


Series

Brainstorm

Source by Dylan Gillis

Mallsyok was one of the many ideas that we came out with each person. We then pooled all of those ideas together so we have discussion on each and everyone of them. It was one of the those exciting moments where we keep using our creativity and critical thinking skills of think of some crazy ideas. Finally we came down to only 5 ✋ideas.

Our favourite tool to do it : Google Sheet


Idea Validation

Source by Glenn Carstens-Peters

We decided to get some public feedbacks on the 5 ideas by creating an online survey form and sending them to friend, family and colleagues. Based on those feedbacks and study of technical complexity, Mallsyok was chosen.

Our favourite tools to do it : Survey Monkey and Google Form.

For Survey Monkey free account, you are only able to collect a certain amount of feedbacks. Due to that, we had to duplicate our form out in Google Form. Moral of the story, always do research of the free tools account of their drawbacks before start using them. Else you would be wasting time in the end.

We asked some of the respondents on how they solve the issue of getting lost in mall. Here are some of their replies:

Visit mall directory panel

Ask officer at information counter

Search in internet

Memorize hard the shop name and floor level 🤔

Give up when cannot locate outlet 😄


Business Writing

Source by Helloquence

Before we start commencing on build Mallsyok, we had to list down 🖋some aspects to be considered and taken care of.

Immediate and long-term objectives

  • What immediate effect on user when using your app?
  • What are the long term goals? Are you trying to create an ecosystem or to start a new consumer habit?

Background and market opportunity

  • Is there a need for this app?
  • Any existing apps that are similar? What are their strengths and weaknesses?

Team organization

  • Who is doing what?

Income and Expenses

  • What are the items you will be spending on?
  • Planning to outsource some task? That’s an expenditure.

Features to be implemented

  • List out the features in order of how important each feature is it

Marketing strategy

  • Social Media? Collaboration? Free-gifts? Blog?

Our favourite tool to do it : Google doc


Wireframe

Wireframe is important to us as it not only helps us to visualise how the final app will look like, but also show us some of the important aspects such as:

  • Space distribution
  • Content prioritization
  • Available functions
  • Intended actions
  • Relationship between screens

Our favourite tool to do it : Balsamiq Mockups 3





Mockups are like skeletons to your app

Mockups

Mockups help to enrich our wireframes. This is the stage where we add colours, fonts, texts, images, logos and anything beautiful to help make the wireframes come to live.

Our favourite tool to do it : Sketch









Adding some paint work to the canvas

Marketing

I guess we struggled a lot in marketing. Having many trials and errors but it did not seem to boost our installs. We have tried Facebook with different settings to boost our post for reaching more targets. Our website was hosted in Heroku and because of this our cost was kept at relatively low. Just the cost of the domain name. We have a wordpress blog where we would populate it with posts of recommended malls and some shopping tips.

Our favourite tools to do it : Facebook, Heroku, WordPress


Team Collaboration

We took this opportunity to try out some of the cool tools in the market to enhance our team collaboration. One important aspects of collaboration is team communication. Slack enables us to organise and keep our discussion according to channels or topics. It can be integrated with third-party plugins (one of my favourites is to-do) to further improve our productivity. Other than that, we use Whatsapp for urgent discussion but most of the time for water-cooling purpose. For group meeting or video call purpose, we choose Skype.

To keep track of our progress and outcome of discussion, we resort to the old fashion way which is using Google Sheet.

As for code collaboration and bug tracker, we use Bitbucket because it offers private repositories for free but limited amount (Special thanks to a colleague of mine).

Our favourite tools to do it : Slack, Whatsapp, Skype, Google Sheet, Bitbucket

Inspiration from other startups

I dedicate this special portion of the post to extend our sincere thank you to 2 startups that have shared with us their knowledge and experience. I guess that is the beauty of the world of startups, where everyone is generous and excited to share anything, hoping people continue to build great things! So thank you and kudos to Rovo and FirstSmile 👏. Strongly recommend to checkout their awesome apps 👍

In the next post, I will dive into each of Mallsyok features and experience of building them 💪

Check me out :


Mallsyok : The Journey

Mallsyok Google PlayStore Banner

Introduction

Mallsyok app was created with a sole purpose which is to help shoppers find their way quicker inside Malaysia’s shopping malls. With a tagline #ShoppingIsNowMoreSyok, this app intends to enhance shoppers’ experience. The last two words of the tagline that is “More Syok” which means you get to enjoy more translate to the name of the app, Mallsyok. Often we feel lost especially nowadays newer malls tend to get bigger in size. This app is an experiment I tried it out with bunch of friends searching other form of excitement in life. Though the adoption is poor but I think the whole project was a success in the sense that we have achieved valuable lessons and experience from it. Hence I decided to share it through this writing.

Update: Unfortunately, we have stopped building new features and maintaining Mallsyok app. But that will not stop me from sharing our experience out 😜

Series

What are the problems?

As mentioned earlier, shoppers tend to get lost inside a big shopping mall. They tend to rely on directory boards which are scarcely available and difficult to locate. At times, these boards would be crowded with anxious shoppers which brings to more disappointment to whole shopping experience. Worst case scenario is that the information on the board is not up-to-date.

Nowadays malls have their own shopping mall apps to show all these information. But that brings shoppers having to install multiple apps for each mall. Would it be great if they would only need to install just ONE app for all the malls.


Features of Mallsyok






Mallsyok supports major shopping malls across Malaysia.

Mallsyok brings shoppers up-to-date directories within their fingertips. Whichever mall they go, they would just need to find what is needed with only a single app.

Shoppers get interactive visual aid from Mallsyok as it provides detailed floor maps.

Mallsyok offers shoppers a way to automatically selects the correct mall by locating their position.

We offer wide range of outlet categories so that shoppers can get the most accurate search result.

Other than directories, we too provide useful informations such as mall contact details, parking rates, opening hours and tips on getting there.

The mentioned features above are already being implemented. On top of that, we have planned more advance features such as bringing shoppers notification of mall events and promotions.

Another cool feature in the list is dynamic navigation. Think of it as a google map, giving you instructions to assist you reaching to your outlet destination inside a mall.

Mallsyok also plans to implement food suggestion feature to help shoppers who have no idea of what to eat.

Shoppers do not need to worry about expensive parking rate when Mallsyok could help estimate the fee and notify them when fee reaching certain amount.


In the next post, I will share about Mallsyok team workflow.

Get the Mallsyok mobile app in Google PlayStore

Click me:

How to do looping in Windows Batch

Source from Tine Ivanič

We need loops in batch file to prevent writing long blocks of code and to do repetitive process. There are 2 ways to do looping in Windows Batch.

Method 1

Use this method for numeric values

FOR /L %%A IN (1,1,200) DO (
ECHO %%A
)
where 1,1,200 means start, increment step and end

Method 2

Use this method for non-numeric values

FOR %%G IN (Sun Mon Tue Wed Thur Fri Sat) DO echo %%G

How to prepare for Embedded C interview

I have compiled some of the common interview questions that you may encounter if you are applying for embedded software position. Hope it benefits you.

Image by Ilya Pavlor at Unsplash

What is the difference between hard real-time and soft real-time OS?

A Hard real-time system strictly adheres to the deadline associated with the task. If the system fails to meet the deadline, even once, the system is considered to have failed. In case of a soft real-time system, missing a deadline is acceptable. In this type of system, a critical real-time task gets priority over other tasks and retains that priority until it completes.

What is volatile ?

Variable that can change unexpectedly

  • hardware registers in peripheral
  • non automatic variable referenced within interrupt service
  • variable shared by multiple task in multiple threaded applications

What is const?

  • Means read only.
const int a; // const integer
Int const a; // const integer
Const int *a; // pointer to const integer. Integer x modify but pointer can
Int * const a; // const pointer integer. Integer can modify, pointer x
Int const * a const; // const pointer to const integer

Can parameter be both const and volatile ?

Yes. Example is read only status register. Volatile cause can change unexpectedly. Const cause program should not change it.

How to declare data

int a ; // an integer
Int *a; // pointer to integer
Int **a; // pointer to pointer to integer
Int a[10]; // array of 10 integers
Int *a[10]; // an array of pointers to integer
Int (*a)[10]; // a pointer to array of 10 integers
Int (a*)(int); // a pointer of function that takes integer argument and returns integer

Explain Union

Useful when need direct access to hardware/memory needed. Share same memory. As long as longest memory.

typedef union{
struct {
unsigned char byte1;
unsigned char byte2;
unsigned char byte3;
unsigned char byte4;
} bytes;
unsigned int dword;
} HW_Register;
HW_Register reg;
reg.dword = 0x12345678;
reg.bytes.byte1 = 1;

Memory segment divided into text, data, heap and stack.

  • Text is the program code.
  • Data is divided into initialised and uninitialised.
  • Initialised means static and global variables initialised with non zero values
  • Uninitialised means static and global variables initialised with zero or not initialised. Also know as BSS — Block Started by Symbol
  • Both Heap and Stack refers to RAM.
  • Heap used for dynamic memory allocation. No enforced pattern to allocation and deallocation. Access is slower.
  • Stack used for static memory allocation. It is reserved in Last In First Out (LIFO). When a function is called, a block is reserved at the top to keep data, when function is returned, the block is then freed.

Assembler, linker and loader

  • Preprocessing is the first pass of any c compilation. It processes include files, conditional compilation instructions and macros.
  • Compilation is the second pass. It takes the output of preprocessor and the source code and generated the assembler source code
  • Assembly is the third stage. It takes the assembly source code and provides an assembly listing with offsets. The output is stored in object file.
  • Listing is the final stage. It takes one or more object files or libraries as input and combines them to produce a single files. In doing so, it resolves references to external symbols, assigns final addresses to procedures / functions and variables and revises code and data to reflect new addresses (a process called relocation).

What is inline function?

  • Inline function is feature to increase execution time of program and reduce function calling overhead.
  • With inline, compiler replaces the function call statement with function code itself. This compile does not have to jump to another location to execute the function.
  • Disadvantage includes increases the executable size due to code expansion.

Explain Macros

Enable assign short names to source code blocks.

#define LEN 128
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define MYMACRO() (func();)

Function of Static?

  • A variable declared static within body of function maintains its value between function invocations.
  • A variable declared static within a module (outside body of function) is accessible by all functions within that module. It is not accessible by functions from other module.
  • Functions declared static within module may only be called by other functions within that module. That is the scope is localised to the module within which it is declared.

How to access fixed memory location

  • Set integer variable at absolute address 0x67a9 to value 0xaa55
Int * ptr;
Ptr = (int *) 0x67a9;
*ptr = 0xaa55;

Standard library header <cstdint>

Int8_t, Int16_t, Int32_t, Int64_t = Signed integer type of width exactly 8, 16, 32, and 64 bits with no padding bit and using 2’s complement for negative values.

Size of variables

  • Char = 1 byte
  • Short / Int = 2 byte
  • Long = 4 byte
  • Pointer (irrespective of type it points to) = 4 bytes (32bit machine), 8 bytes (64bit machine)

NULL and Void Pointer

  • Null pointer is a special reserved value of a pointer. A pointer of any type has such a reserved value. Formally, each specific pointer type (int *, char * etc.) has its own dedicated null-pointer value. Conceptually, when a pointer has that null value it is not pointing anywhere.
  • Void pointer is a specific pointer type — void * — a pointer that points to some data location in storage, which doesn’t have any specific type.

Is Count Down_to_Zero Loop better than Count_Up_Loops?

  • Count down to zero loops are better.
  • Reason behind this is that at loop termination, comparison to zero can be optimized by the compiler.
  • Most processors have instruction for comparing to zero. So they don’t need to load the loop variable and the maximum value, subtract them and then compare to zero. That is why count down to zero loop is better.

Can structures be passed to the functions by value?

  • Passing structure by its value to a function is possible, but not a good programming practice.
  • First of all, if we pass the structure by value and the function changes some of those values, then the value change is not reflected in caller function.
  • Also, if the structure is big, then passing the structure by value means copying the whole structure to the function argument stack which can slow the program by a significant amount.

Reference:

http://a4academics.com/interview-questions/57-c-plus-plus/722-embedded-c-interview-questions?showall=&limitstart=

How calculate time difference in Windows Batch?

Source from Sonja Langford

A small piece of code to determine time difference. Could be useful and integrated to your batch file.

@echo off
rem Get start time:
for /F “tokens=1-4 delims=:.,” %%a in (“%time%”) do (
set /A “start=(((%%a*60)+1%%b %% 100)*60+1%%c %% 100)*100+1%%d %% 100”
)
rem Any process here…
TIMEOUT /T 60
TIMEOUT /T 1
rem Get end time:
for /F “tokens=1-4 delims=:.,” %%a in (“%time%”) do (
set /A “end=(((%%a*60)+1%%b %% 100)*60+1%%c %% 100)*100+1%%d %% 100”
)
rem Get elapsed time:
set /A elapsed=end-start
rem Show elapsed time:
set /A hh=elapsed/(60*60*100), rest=elapsed%%(60*60*100), mm=rest/(60*100), rest%%=60*100, ss=rest/100, cc=rest%%100
if %mm% lss 10 set mm=0%mm%
if %ss% lss 10 set ss=0%ss%
if %cc% lss 10 set cc=0%cc%
echo %hh%:%mm%:%ss%,%cc%
PAUSE

I never written a line of code. So what is Git? Pt.1

Fire escape procedure for Git users. No, seriously.

Before I begin, this article explains the most fundamental of stuff of Git, how to use and why you should use it. So if you’re new to coding, this article is just right for you 👍 Let’s Git started! :


OK. So what is Git?

Git is a version control system for tracking files changes in your computer.

And what is version control system?

Imagine you are spending hours of coding to fix an issue on your project. The code fix works and issue has been gone. Hooray~! Time to grab a beer 🍺 to celebrate. Unfortunately something nasty happens and your computer crashes. When your computer boots up, seems like the changes are not there anymore. Your hours of effort gone and probably you need put that beer down and redo everything.

Now if you have been using version control system, code fix checked ✅, issue fixed ✅ and then you save your changes in the another computer which is call a server ✅. Nasty stuff happens, no problem. Simply download your recent changes back to your local computer ✅. Sit back and enjoy your beer 🍺.

Another example could be you have a good version of a file. Then you make some changes here and there and your project breaks 😢. So you keep pressing CTRL + Z multiples times but seems unable to retrieve back that good version of the file. Now with a version control system, you have saved snapshot of the file version before you do your spaghetti changes, all you need to do is simply rollback and you’re good to go at it for round 2.

In Git we trust!

Git and Github??

Now I am going to introduce you to another term call Github. And no, Git and Github are 2 different things 😕.

Again Git is a tool for you to track changes. Github on the other hand, is a web-based git repository hosting service. In other words, Github allows you to keep your code in the server and track it using git.

You can always use Git without Github. But then all your copies of the code is in your local computer. If you wish to have a backup and store in server, then you need Github. Also with Github, you can share your project with your friends, or even working together 💪.

Let’s Git Started

  1. First we need to create an account in Github


2. Then we need to create a repository (basically a project name) . Description is optional. You can set your repository as Public or Private as what the description in the snapshot shows. For Public, anyone can view your project and also make changes to it (aka Commit)but only admin (which is you) approves or rejects this changes. This will be our remote repository.


3. Once repository is created. Copy the link highlighted in blue below. We will be needing this later 😅


4. At your local computer 💻, create an empty folder (in this case im using GitLearning_Local). Launch terminal, change directory to this folder using command: cd <foldername>


5. We then use command git init to ask git start tracking this project. This will be our local repository. Remote and local repository, get it?

git init


6. We then need to tell git the link to our remote repository (link is from step 3). Simply run command git remote add origin <link>. We then use git remote -v to confirm the correct link

git remote add origin <link>
git remote -v


7. Now that our remote and local repo has been set up. Time to take a tea break 🍵. Pat yourself on the shoulder 👏. Now it’s time to enjoy the benefits of git. Let’s create a new file call file1.txt with content “content 1”. If i use git status , i should able to see the message

git status


8. Now Git is telling you that there is an untracked file. We should add this for Git to start tracking the changes in this file. [In Git language, we call this add to staging area] Hence the command, git add <filename> to add for tracking. If you have multiple untracked files, you can simply use git add . which means add all the files. [FYI 🤔, to UNDO git add, use command git reset <file>, or git reset all changes]. Let’s try git status again and now Git is telling in the staging area, there is a new file.

git add <filename>
git add .


9. We are ready now to save our changes to our local repository. But we need to attach a commit message that describes what this change is all about.

git commit <file> -m "<message>"


10. Finally we are just one command away from saving our changes to our remote repository.

git push origin master


11. Now if we refresh our remote repository in Github, the file should show up there.

Well done! You have successfully create a new file in both local and remote repo

12. Now let’s say we have an updated version of file1.txt with content “version 2”. If we run git status. We should see the status below: Git is telling us we have modified our file1.txt, and we should either add to staging or discard changes using git checkout


13. We can use git diff <filename>, a handy command to see the difference between updated file and the saved in local repo.

git diff <filename>


14. What happens next? Well you know the drill: git add — commit — push! Now if you enter git log <filename>, you should be able to see the history of changes made to the file: You might be wondering what is the random value appear next to commit? Well each individual changes has its own commit hash which allows them to differentiate from each other.


15. Imagine that you have made a mistake in version 2 of file1.txt and you would like to roll back to the previous version. You can remove the last commit by simply running git revert HEAD and don’t forget push to remote repo.

git revert HEAD


Graphical illustration of basic Git commands

There you go, we have covered the basic bread and butter of using Git. Of course there are more handy commands that will be covered in Pt.2. But I hope this step by step tutorial is beneficial 💪for those who have never heard or use Git.

If you like this articles and would like to see more of it in future, hit the clap 👏 button below!

Thanks for reading! 😃

Design a site like this with WordPress.com
Get started