Comparing Flutter code to Javascript - LLF #6

Keff - Oct 11 '21 - - Dev Community

Hey there 👋

I'm back with another entry to this series, so... Javascript right? We all love and hate it, yet have no choice but to use it if we want to create interactive websites (kinda). As a result, many developers know Javascript or have at least used it on some occasion.

Consider this post as a comparison between some common aspects of both JS and Dart applications (like async code, handling lists/arrays).

Table Of Contents

Entrypoints

The first step to creating an app is launching/starting it. In dart, an entry-point function is required for all applications, in contrast to Javascript where it's up to you to define such a function. Note that if you use a framework it might require you to define an entry-point of some sort.

Let's see how Dart does it and then show some examples of how we do that in javascript and some frameworks.

Dart

In dart, all applications are required to start with a main() function.

void main() {
    // Your initial code would go here
}
Enter fullscreen mode Exit fullscreen mode

Flutter

In flutter, we are also required to define a main() function. But to start the app we must call the runApp() function. This will bootstrap and start our app:

void main() {
    runApp(const MyApp());
}
Enter fullscreen mode Exit fullscreen mode

Javascript

Javascript is not as strict and doesn't force us to write a main function or any kind of entry-point really. Some might start directly when the script loads, or maybe wait until the page is fully loaded. Others might only start when a button is clicked and so on.

When script loads:

function startHere() {
    // Your initial code would go here
}

startHere(); // Called when script is loaded
Enter fullscreen mode Exit fullscreen mode

When page loads:

function startHere() {
    // Your initial code would go here
}

document.addEventListener('load', () => {
    // Called when the page fully loaded all the HTML and external resources
});
Enter fullscreen mode Exit fullscreen mode

Angular

Angular does not require us to define an entry-point function, but it requires us to create a main file where we will bootstrap/start our application. That file would look something like this:

// imports...

platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch(err => console.error(err));
Enter fullscreen mode Exit fullscreen mode

Angular uses TypeScript but it's the same drill.

React Native

React native somewhat forces you to create an entry-point in the form of a React Component.

import React from 'react';
import { Text, View } from 'react-native';

const HelloWorldApp = () => {
  return (
    <View
      style={{
        flex: 1,
        justifyContent: "center",
        alignItems: "center"
      }}>
      <Text>Hello, world!</Text>
    </View>
  )
}
export default HelloWorldApp;
Enter fullscreen mode Exit fullscreen mode

Logging

Dart does not offer such a variety of logging possibilities as the Javascript console. We are limited to just a single method print, without adding external packages (like logger).

Dart

var example = '"I will, I do, I can, I should could do, yeh"';
print('I can only log a single string, but can easily interpolate variables like this: $example');
Enter fullscreen mode Exit fullscreen mode

JavaScript

let example = '"I will, I do, I can, I should could do, yeh"';
console.log('I can only log more than a single string, ', `but can easily interpolate variables like this: ${example}`);
Enter fullscreen mode Exit fullscreen mode

Functions

Javascript

// Regular function
function canFly(player) {
    return player.hasWings && !player.isAChicken;
}

// Arrow function
const canRespawn = (player) => player.isInmortal;
Enter fullscreen mode Exit fullscreen mode

Dart

canFly(Player player) {
    return player.hasWings && !player.isAChicken;
}

// or specifying the type
bool canFly(Player player) {
    return player.hasWings && !player.isAChicken;
}

// Arrow function
bool canRespawn = (Player player) => player.isInmortal;
Enter fullscreen mode Exit fullscreen mode

Boolean checks

In Javascript 0, null, undefined, an empty string ('') are all evaluated as false. And 1 and any other non-null value is evaluated as true.

Just a meme image on javascript bool checks

Dart only considers the boolean value true as true. So let's have a close look at how to perform some common checks:

Javascript

let undefinedValue = undefined;
let nullValue = null;
let value = 0;
let emptyString = '';

if(!undefinedValue) return;
if(!nullValue) return;
if(!value) return;
if(!emptyString) return;
Enter fullscreen mode Exit fullscreen mode
  • 0, null, undefined, and '' are all being treated as false.

Dart

var nullValue = null;
int value = 0;
String emptyString = '';

if(nullValue == null) return;
if(value == 0) return;
if(emptyString.isEmpty) return;
Enter fullscreen mode Exit fullscreen mode

As you can see, in Dart we must implicitly check if it's a certain value. Nothing is treated as a boolean, except booleans themselves (makes sense).

  • We must use the == equality operator to implicitly check for null, 0, and other values
  • For empty string we can use the built-in isEmpty() method

Futures/Promises

Both Javascript and Dart support single-threaded execution. Javascript offers the Promise object to handle this, while in Dart we use Future objects. These classes represent the eventual completion or failure of some operation.

We commonly use Futures and Promises objects when calling remote APIs via HTTP requests, as they take a long time to complete. Synchronously doing this operation would freeze the application until the operation either fails or completes.

Javascript


function answerOfLife() {
    const url = 'https://answeroflife.ai/give-it-to-me';
    return fetch(url)
        .then(result => result.json());
}

const result = answerOfLife()
    .then(result => result.answer)
    .catch(error => console.error(error));
Enter fullscreen mode Exit fullscreen mode

Dart

import 'package:http/http.dart' as http;
import 'dart:convert';

Future<Response> answerOfLife() {
    const url = 'https://answeroflife.ai/give-it-to-me';
    return http.get(url)
        .then((response) =>  jsonDecode(response.body));
}

void main() {
    var result = answerOfLife()
        .then((response) => response.answer)
        .catchError((error) => print(error));
}
Enter fullscreen mode Exit fullscreen mode

Did you know that you can create Web Apps with Flutter?

It seems it's not 100% ready for big scale applications, though it does the job with some work on our part.


Async/Await

If you are familiar with Javascript async/await, Dart is almost exactly the same. We mark the function with the async keyword, then we can use the await keyword to wait for promises/futures to complete.

Javascript


function answerOfLife() {
    const url = 'https://answeroflife.ai/give-it-to-me';
    return fetch(url)
        .then(result => result.json());
}

async function main() {
    try {
        const result = await answerOfLife().then(result => result.answer);
    } catch(e) {
        console.error(error);
    }
}
Enter fullscreen mode Exit fullscreen mode

Dart

import 'package:http/http.dart' as http;
import 'dart:convert';

Future<Response> answerOfLife() {
    const url = 'https://answeroflife.ai/give-it-to-me';
    return http.get(url)
        .then((response) => jsonDecode(response.body));
}

void main() async {
    try {
        const result = await answerOfLife().then(result => result.answer);
    } catch(e) {
        print(error);
    }
}
Enter fullscreen mode Exit fullscreen mode

Arrays/Lists

Dart handles arrays quite similarly to javascript, with some differences. Let's take a look at some of the most common list operations.

Creating arrays

Dart

// Typed list
List<int> steps = [1, 2, 4, 8, 16, 32, 64];

// Untyped list
List stepsAndBabyNames = [1, 2, 4, 'Jonathan', 8, 'Joseph', 16, 32, 'Tommy', 64];
Enter fullscreen mode Exit fullscreen mode

Javascript

const steps = [1, 2, 4, 8, 16, 32, 64];
const stepsAndBabyNames = [1, 2, 4, 'Jonathan', 8, 'Joseph', 16, 32, 'Tommy', 64];
Enter fullscreen mode Exit fullscreen mode

Iterating arrays

Dart

// Using for-in loop
for(var step in steps) {
    print('Step: $step');
}

// Clasic for 
for(int i = 0; i < steps.length; i++) {
    print('Step: ${steps[i]}');
}

// forEach
steps.forEach((step) => print('Step: $step'));
Enter fullscreen mode Exit fullscreen mode

Javascript

// Using for-in loop
for(let step in steps) {
    console.log(`Step: ${step}`);
}

// Clasic for 
for(let i = 0; i < steps.length; i++) {
    console.log(`Step: ${steps[i]}`);
}

// forEach
steps.forEach((step) => console.log(`Step: $step`));
Enter fullscreen mode Exit fullscreen mode

Map items

Dart

steps = steps.map((step) => step * step).toList();
Enter fullscreen mode Exit fullscreen mode

In dart we need to call toList to convert back to a List, as map returns a lazy Iterable (docs))

Javascript

steps = steps.map((step) => step * step);
Enter fullscreen mode Exit fullscreen mode

Filter items

Dart

steps = steps.where((step) => step > 16).toList();
Enter fullscreen mode Exit fullscreen mode

Javascript

steps = steps.filter((step) => step > 16);
Enter fullscreen mode Exit fullscreen mode

Get a part of the list

Dart

steps.sublist(2, 4);
Enter fullscreen mode Exit fullscreen mode

Javascript

steps.splice(2, 4);
Enter fullscreen mode Exit fullscreen mode

Item exists

Dart

steps.contains(16);
Enter fullscreen mode Exit fullscreen mode

Javascript

steps.indexOf(16) != -1;
steps.includes(16);
Enter fullscreen mode Exit fullscreen mode

Find index of item

Dart

steps.indexOf(16);
Enter fullscreen mode Exit fullscreen mode

Javascript

steps.indexOf(16);
Enter fullscreen mode Exit fullscreen mode

Find single item

Dart

steps.firstWhere((step) => step == 16);
Enter fullscreen mode Exit fullscreen mode

Javascript

steps.find((step) => step == 16).toList();
Enter fullscreen mode Exit fullscreen mode

Has items

Dart

steps.isNotEmpty;
Enter fullscreen mode Exit fullscreen mode

Javascript

steps.length > 0;
Enter fullscreen mode Exit fullscreen mode

Maps/Objects

Most languages offer some sort of data structure to create unstructured data, dart is not different and offers the Map type.

Creating maps

Dart

Map<String, dynamic> info = {
    'name': 'Samuel',
    'age': 27,
};
Enter fullscreen mode Exit fullscreen mode

Javascript

const info = {
    name: 'Samuel',
    age: 27,
};
Enter fullscreen mode Exit fullscreen mode

Serializing/Deserializing JSON

Dart

import 'dart:convert';

Map<String, dynamic> info = {
    'name': 'Samuel',
    'age': 27,
};

var encodedString = json.encode(info);
var decodedMap = json.decode(infoString);
Enter fullscreen mode Exit fullscreen mode

Javascript

const info = {
    name: 'Samuel',
    age: 27,
};

const encodedString = JSON.stringify(info);
const decodedMap = JSON.parse(infoString);
Enter fullscreen mode Exit fullscreen mode

Running an app

Running apps differs from language to language, and from framework to framework.

Flutter

Let's first see how we do it in flutter:

  • Using the run utility in the IDE
  • Executing flutter run in the project root

React Native

In react native we:

  • use npm script npm run
  • or yarn npm run

Angular

  • Run ng serve in the project root
  • Execute npm start

Learning more

Summary

As seen in the post, Javascript and Dart have quite a few things in common. But differ in some aspects, such as handling booleans or working with arrays. This post just scratches the surface so let me know if you would like a follow-up!

If you found this post useful, please consider checking my previous post. It's a similar format to this one but comparing the theming/styling aspects of Flutter against CSS.

And as always, thanks for reading. And remember to comment if you have any suggestions, doubts, or something you would like me to cover in these posts.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .