Building Mobile Apps with

and

Simon MacDonald

simonmacdonald.com

@macdonst

Building Nice Looking Mobile Apps is Hard!

In my mind, I envision something like this:

Sadly in reality it ends up looking like this:

Why is design so hard?

Because…

…and I'm pretty lazy

So, that's why I use

to save myself a ton of work

Framework7 is an open source HTML framework to develop apps with iOS & Android native look and feel

iOS

Material

  <div id="app">
    <!-- Statusbar -->
    <f7-statusbar></f7-statusbar>
    <!-- Main View -->
    <f7-view id="main-view" url="/" main>
      <f7-page>
        <f7-navbar sliding title="Home"/>
        <f7-block-title>
          {{ title }}
        </f7-block-title>
        <f7-block inner>
          Lorem ipsum dolor sit amet, consectetur 
          adipisicing elit. Distinctio est aliquam 
          officiis quaerat placeat, cum explicabo 
          magni soluta totam maxime autem minima 
          accusamus eos suscipit dignissimos
          corporis modi voluptatum fugiat!
        </f7-block>
      </f7-page>
    </f7-view>
  </div>

Framework7 Example

That's pretty neat, but the only tag I recognize is <div/>

I guess I put the cart before the horse and I should talk about

Vue is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is very easy to pick up and integrate with other libraries or existing projects. On the other hand, Vue is also perfectly capable of powering sophisticated Single-Page Applications when used in combination with modern tooling and supporting libraries.

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>My App</title>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

JavaScript

import Vue from 'vue';
import Framework7 from 'framework7/dist/framework7.esm.bundle.js';
import Framework7Vue from 'framework7-vue/dist/framework7-vue.esm.bundle.js';
import Framework7Styles from 'framework7/dist/css/framework7.css';
import IconsStyles from './css/icons.css';
import AppStyles from './css/app.css';
import Routes from './routes.js';
import App from './app';

// Init F7 Vue Plugin
Vue.use(Framework7Vue, Framework7);

// Init App
const baseApp = new Vue({
  el: '#app',
  template: '<app/>',
  framework7: {
    id: 'io.framework7.testapp', // App bundle ID
    name: 'Framework7', // App name
    theme: 'auto', // Automatic theme detection
    routes: Routes
  },
  components: {
    app: App
  }
});

App.vue

<template>
  <div id="app">
    <!-- Statusbar -->
    <f7-statusbar></f7-statusbar>
    <!-- Main View -->
    <f7-view id="main-view" url="/" main>
      <f7-page>
        <f7-navbar sliding title="Home"></f7-navbar>
        <f7-block-title>{{ title }}</f7-block-title>
        <f7-block inner>
        Lorem ipsum dolor sit amet, consectetur adipisicing elit.
        Distinctio est aliquam officiis quaerat placeat, cum explicabo magni
        soluta totam maxime autem minima accusamus eos suscipit dignissimos
        corporis modi voluptatum fugiat!
        </f7-block>
      </f7-page>
    </f7-view>
  </div>
</template>
<script>
  export default {
    name: 'App',
    data() {
      return {
        title: 'Hello World',
      };
    }
  };
</script>

Wow! So you are saying I can compose my UI of reusable components?

That's exactly what I'm saying!

Let's create new component in our app to show some art

Data Store

Vue.js provides two way data binding so we'll setup a store in our main.js to store the art.

import Vue from 'vue';
import Framework7 from 'framework7/dist/framework7.esm.bundle.js';
import Framework7Vue from 'framework7-vue/dist/framework7-vue.esm.bundle.js';
import Framework7Styles from 'framework7/dist/css/framework7.css';
import IconsStyles from './css/icons.css';
import AppStyles from './css/app.css';
import Routes from './routes.js';
import App from './app';

// Store
window.pictureStore = JSON.parse(localStorage.getItem('pictures')) || [];

// Init F7 Vue Plugin
Vue.use(Framework7Vue, Framework7);

// Init App
const baseApp = new Vue({
  el: '#app',
  template: '<app/>',
  framework7: {
    id: 'io.framework7.testapp', // App bundle ID
    name: 'Framework7', // App name
    theme: 'auto', // Automatic theme detection
    routes: Routes
  },
  components: {
    app: App
  }
});

Data Refresh

  • Add pull to refresh to page
  • Add pictureStore to globals
  • Create onRefresh method
<template>
  <f7-page ptr @ptr:refresh="onRefresh">
    <f7-navbar sliding title="Home"></f7-navbar>
    <f7-block-title>{{ title }}</f7-block-title>
    <f7-block inner>
      Lorem ipsum dolor sit amet, consectetur adipisicing elit.
      Distinctio est aliquam officiis quaerat placeat, cum explicabo magni
      soluta totam maxime autem minima accusamus eos suscipit dignissimos
      corporis modi voluptatum fugiat!
    </f7-block>
  </f7-page>
</template>
<script>
  /* globals pictureStore */

  export default {
    name: 'App',
    data () {
      return {
        title: 'Hello World',
      };
    }
  };
</script>
<script>
  /* globals pictureStore */
  export default {
    name: 'App',
    data () {
      return {
        title: 'Hello World',
      };
    },
    methods: {
      onRefresh (event, done) {
        fetch('http://localhost:8974')
        .then(response => response.json())
        .then((data) => {
          pictureStore.unshift(data);
          localStorage.setItem('pictures', JSON.stringify(pictureStore));
          done();
        });
      }
    }
  };
</script>

Picture List

  • Create a PictureList.vue file
  • Populate the template
  • Add the script tag
<template>
  <div>
    <f7-card v-for="pics in pictures" v-bind:key="pics.artist">
      <f7-card-header>
        <div class="facebook-name">{{ pics.artist }}</div>
      </f7-card-header>
      <f7-card-content>
        <img :src="pics.image" width="100%"/>
      </f7-card-content>
    </f7-card>
  </div>
</template>
<template>
  <div>
    <f7-card v-for="pics in pictures" v-bind:key="pics.artist">
      <f7-card-header>
        <div class="facebook-name">{{ pics.artist }}</div>
      </f7-card-header>
      <f7-card-content><img :src="pics.image" width="100%"/></f7-card-content>
    </f7-card>
  </div>
</template>
<script>
  /* global pictureStore */
  export default {
    name: 'PictureList',
    data () {
      return { pictures: window.pictureStore, };
    }
  };
</script>

Use PictureList

  • Add <picture-list/> tag
  • Import the PictureList
  • Include picture-list component
<template>
  <f7-page ptr @ptr:refresh="onRefresh">
    <f7-navbar sliding title="Home"></f7-navbar>
    <picture-list/>
  </f7-page>
</template>
<script>
  /* globals pictureStore */
  import PictureList from '../PictureList';
  export default {
    name: 'App',
    data() {
      return {
        title: 'Hello World',
      };
    },
    methods: {
      onRefresh(event, done) {
        fetch('http://localhost:8974')
        .then(response => response.json())
        .then((data) => {
          pictureStore.unshift(data);
          localStorage.setItem('pictures', JSON.stringify(pictureStore));
          done();
        });
      },
    }
  };
</script>
<script>
  /* globals pictureStore */
  import PictureList from '../PictureList';
  export default {
    name: 'App',
    data () {
      return {
        title: 'Hello World'
      };
    },
    components: {
      'picture-list': PictureList
    },
    methods: {
      onRefresh (event, done) {
        fetch('http://localhost:8974')
        .then(response => response.json())
        .then((data) => {
          pictureStore.unshift(data);
          localStorage.setItem('pictures', JSON.stringify(window.pictureStore));
          done();
        });
      }
    }
  };
</script>

That's awesome! How do the single file components get included in the app?

Oh, that's easy. Just use Webpack

Nope!

Wait, don't run away now! We've created some templates to get you started!

In each of the following templates we've done the heavy lifting setting up

Plus, we've added Mocha for unit testing and Nightwatch for end to end testing.

As well as setting up default layouts using

Honestly, we don't care if you use all this stuff in the template or import your own.

The issue we are trying to combat here is JavaScript Paralysis where you don't know how to get started.

Blank

Tabs

Split Pane iOS

Split Pane Android

Getting Started

npm install cordova -g
cordova create MyAppName --template=phonegap-template-vue-f7-split-panel
cd MyAppName
npm install
npm run dev

You can then open the app in your browser by visiting localhost:8080

Deploy your app

// Develop in the iOS simulator
cordova platform add ios
npm run cordova-run-ios

// Develop in the Android emulator
cordova platform add android
npm run cordova-run-android

// Build for production, removes hot module reloading server
npm run build

Then you are ready to deploy your app to the web, iOS App Store or Google Play Store.

The Future

  • We're adding Progressive Web App support
  • Decrease boilerplate code using Workbox
  • Templates that score high on Lighthouse

The End

Resources

Building Mobile Apps with Vue.js

By Simon MacDonald

Building Mobile Apps with Vue.js

FITC Vue Spotlight

  • 4,398