Skip to main content

Electron for Cross Platform Applications

Shenzhen, China

Github Repository

Installation

There is already a version 14 available of Electron - but I am following along a tutorial using Version 4. So for now I will initialize the project and install this specific version:

npm init -y
npm install --save-dev electron@4.1.4

Add the following lines to the scripts section of your package.json to be able to start Electron with npm:

"scripts": {
    "start": "electron ."
  },

Loading a Webpage

Now we need to create a hello world index.js file in the root directory of our app:

const { app, BrowserWindow } = require('electron');

function createWindows() {
  let welcomeWindow =  new BrowserWindow();
  welcomeWindow.loadURL('https://wiki.instar.com/de/');
}

app.on('ready', createWindows);

Now all you need to do is to start your application with the npm script we added and your Electron app will come up with a webView of the web content you defined above in the createWindow function:

Getting started with Electron4

Loading a File

Now we can use Electron as a wrapper for some local content:

const { app, BrowserWindow } = require('electron');

function createWindows() {
  let welcomeWindow =  new BrowserWindow();
  welcomeWindow.loadFile('./public/index.html');
}

app.on('ready', createWindows);

Now create a folder inside the app root called public and add a file index.html inside it:

<!DOCTYPE html>
<html lang="en">
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello, World!</title>
  </head>
  <body>
    <h1>Hello, World!</h1>
    <p>Electron File Wrapper</p>
  </body>
</html>

Run npm start again - this time you should see the rendered HTML page above.

Electron BrowserWindow

I noticed that the default Window that opens up when I start Electron is too small for the content I am loading. The BrowserWindow API allows us to configure a lot of things about our app - for example it's width and height:

const { app, BrowserWindow } = require('electron');

function createWindows() {
  let welcomeWindow =  new BrowserWindow(
    {
      width: 1024,
      height: 768,
      center: true,
      resizable: true,
      moveable: true,
      minimizable: true,
      maximizable: true,
      fullscreen: false,
      fullscreenable: true,
      minWidth: 800,
    }
  );

  welcomeWindow.loadFile('./public/index.html');
}

app.on('ready', createWindows);

Instance Events

Objects created with new BrowserWindow emit the events that we can react to. An example is - we want to wait for our page to load completely before displaying the webView Window. For this we just have to set our window to not show until an eventListener is triggered:

const { app, BrowserWindow } = require('electron');

function createWindows() {
  let welcomeWindow =  new BrowserWindow(
    {
      width: 1024,
      height: 768,
      center: true,
      resizable: true,
      moveable: true,
      minimizable: true,
      maximizable: true,
      fullscreen: false,
      fullscreenable: true,
      minWidth: 800,
      show: false
    }
  );

  welcomeWindow.loadFile('./public/index.html');

  welcomeWindow.on('closed', () => {
    welcomeWindow = null
  });

  welcomeWindow.once('ready-to-show', ()=> {
    welcomeWindow.show();
  });
}

app.on('ready', createWindows);

This version of our app now also listens for the closed event and will free up all it's memory once the Windows was closed.

Working with Multiple Windows

I now want to add a second window to my Electron app. Let's say I have a welcome page and from there I want to be able to start a dashboard. I create the dashboard.html file inside the /public folder:

<!DOCTYPE html>
<html lang="en">
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Dashboard</title>
  </head>
  <body>
    <h1>Dashboard</h1>
    <p>This is the Dashboard Page</p>
  </body>
</html>

Now we need to add the function that will add our new browser window:

const { app, BrowserWindow } = require('electron');

function createWindows() {
  let welcomeWindow =  new BrowserWindow(
    {
      width: 600,
      height: 400,
      center: true,
      backgroundColor: '#2f78f9',
      hasShadow: true,
      darkTheme: true,
      resizable: true,
      moveable: true,
      minimizable: true,
      maximizable: true,
      fullscreen: false,
      fullscreenable: true,
      minWidth: 800,
      show: false,
      alwaysOnTop: true
    }
  );

  welcomeWindow.loadFile('./public/index.html');

  welcomeWindow.on('closed', () => {
    welcomeWindow = null
  });


  let dashboardWindow = new BrowserWindow({
      width: 1024,
      height: 768,
      x: 0,
      y: 0,
      resizable: true,
      moveable: true,
      minimizable: true,
      maximizable: true,
      kiosk : true,
      fullscreenable: true,
      frame: true,
      transparent: false,
      show: false
  });

  dashboardWindow.loadFile('./public/dashboard.html');

  welcomeWindow.once('ready-to-show', ()=> {
    welcomeWindow.show();
    dashboardWindow.show();
    dashboardWindow.maximize();
  });

  dashboardWindow.on('closed', () => {
    dashboardWindow = null
  });
}

app.on('ready', createWindows);

Window Timing

Set a timer on the welcome screen to have it appear after the dashboard loaded and then have it disappear again after a timeout:

const { app, BrowserWindow } = require('electron');

function createWindows() {
  let welcomeWindow =  new BrowserWindow(
    {
      width: 600,
      height: 400,
      center: true,
      backgroundColor: '#2f78f9',
      hasShadow: true,
      darkTheme: true,
      resizable: true,
      moveable: true,
      minimizable: true,
      maximizable: true,
      fullscreen: false,
      fullscreenable: true,
      minWidth: 800,
      show: false,
      alwaysOnTop: true
    }
  );

  welcomeWindow.loadFile('./public/index.html');

  welcomeWindow.on('closed', () => {
    welcomeWindow = null
  });


  let dashboardWindow = new BrowserWindow({
      width: 1024,
      height: 768,
      x: 0,
      y: 0,
      resizable: true,
      moveable: true,
      minimizable: true,
      maximizable: true,
      kiosk : true,
      fullscreenable: true,
      frame: true,
      transparent: false,
      show: false
  });

  dashboardWindow.loadFile('./public/dashboard.html');

  welcomeWindow.once('ready-to-show', ()=> {
    dashboardWindow.show();
    dashboardWindow.maximize();

    setTimeout(() => {
      welcomeWindow.show();
          setTimeout(() => {
            welcomeWindow.hide();
          }, 5000);
    }, 1000);
  });

  dashboardWindow.on('closed', () => {
    dashboardWindow = null
  });
}

app.on('ready', createWindows);

ipcMain and ipcRenderer

The ipcMain module is an Event Emitter. When used in the main process, it handles asynchronous and synchronous messages sent from a renderer process (web page). Messages sent from a renderer will be emitted to this module.

The ipcRenderer module is an EventEmitter. It provides a few methods so you can send synchronous and asynchronous messages from the render process (web page) to the main process. You can also receive replies from the main process.

I now want to use the ipcMain module to handle the closing of the welcome screen - before I used the setTimeout function. For this I am going to add the following to the index.js file and remove the welcomeWindow.hide() timer:

const { app, BrowserWindow, ipcMain } = require('electron');

...

ipcMain.on('closeWelcomeWindow', (event) => {
    welcomeWindow.hide();
  });

This function will hide the welcome screen when an event with the name closeWelcomeWindow is triggered - we will have to add this event to our app.

We also have to add add the Node.js integration to our welcome screen to be able to execute Node.js functions from within this window - like importing Electron code:

function createWindows() {
  let welcomeWindow =  new BrowserWindow(
    {
      ...
      webPreferences: {
        nodeIntegration: true
      },
      ...
    }
  );

Now to connect our web content we need to add some javascript to the welcome page:

<!DOCTYPE html>
<html lang="en">
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello, World!</title>
  </head>
  <body>
    <h1>Hello, World!</h1>
    <p>Electron File Wrapper</p>
    <button class="button" type="button" id="closeButton">
        Close
    </button>
    <script>
      const { ipcRenderer } = require('electron');
      document.querySelector('#closeButton').addEventListener('click', () => {
        ipcRenderer.send('closeWelcomeWindow')
      });
    </script>
  </body>
</html>

Now we have a button on our welcome page that, when clicked, will emit the closeWelcomeWindow event that our electron process is waiting for to close the welcome screen!