Majid Hajian
Majid Hajian is a passionate software developer with years of developing and architecting complex web and mobile applications. He is passionate about web platform especially flutter, IoT, PWAs, and performance.
Majid Hajian
mhadaily
Credit to: JDominik Roszkowski - https://codepen.io/orestesgaolin/pen/ExVboMY
https://chrome-trex-flutter.netlify.app/#/
Credit to: Joshua de Guzman - https://codepen.io/joshuadeguzman/pen/jObrzJB
https://nike-shop-flutter.netlify.app
Credit to: Zoey Fan - https://codepen.io/zoeyfan/pen/ExVaXGK
https://gooey-edge-flutter.netlify.app
mhadaily
A PWA built with Flutter
Single Page Application
Existing Mobile Applications
At this time
mhadaily
import 'package:flutter/material.dart';
MaterialApp(
ThemeData(
name: "Majid Hajian",
location: "Oslo, Norway",
description: '''
Google Developer Expert
Passionate Software engineer,
Community Leader, Author and international Speaker
''',
main: "Flutter/Dart, PWA, Performance",
homepage: "https://www.majidhajian.com",
socials: {
twitter: "https://www.twitter.com/mhadaily",
github: "https://www.github.com/mhadaily"
},
author: {
Pluralsight: "www.pluralsight.com/authors/majid-hajian",
Apress: "Progressive Web App with Angular, Book",
PacktPub: "PWA development",
Udemy: "PWA development",
}
founder: "Softiware As (www.Softiware.com)"
devDependencies: {
tea: "Ginger",
mac: "10.14+",
},
community: {
MobileEraConference: "Orginizer",
FlutterVikings: "Orginizer",
FlutterDartOslo: "Orginizer",
GDGOslo: "Co-Orginizer",
DevFestNorway: "Orginizer",
...more
}));
Find me on the internet by
https://fluttervikings.com/
Reliable
Fast
Engaging
Reliable
Linkable
Engaging
Secure
Progressive by nature
Native-like User Experince
Responsiveness
Fast
Discoverable
mhadaily
mhadaily
mhadaily
mhadaily
mhadaily
mhadaily
mhadaily
mhadaily
mhadaily
self.addEventListener("install", (event) => {
}
self.addEventListener("activate", function(event) {
}
self.addEventListener("fetch", (event) => {
}
mhadaily
fetch('http://example.com/movies.json')
.then(response => response.json())
.then(data => console.log(data));
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
mhadaily
// Cache API
caches.open(cacheName)
.then(function(cache) {
return cache.addAll(
[
'/css/bootstrap.css',
'/css/main.css',
'/js/bootstrap.min.js',
'/js/jquery.min.js',
'/offline.html'
]
);
})
https://developer.mozilla.org/en-US/docs/Web/API/Cache
mhadaily
mhadaily
mhadaily
The Web App Manifest is a JSON text file following Web App Manifest specification
that provides information about an application such as its name, author, icons, and description.
mhadaily
<link rel="manifest" href="/app.webmanifest">
<link rel="manifest" href="/manifest.json">
mhadaily
{
"short_name": "Flutter",
"name": "Flutter: Amazing Technology!",
"icons": [
{
"src": "/images/icons-192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "/images/icons-512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": "/?source=pwa",
"background_color": "#3367D6",
"display": "standalone",
"scope": "/",
"theme_color": "#3367D6",
"description": "Flutter pwa information",
}
mhadaily
mhadaily
mhadaily
https://dart.dev/tools/dart2js
dart2js -O2 -o test.js test.dart
https://developers.google.com/web/tools/workbox
npm install workbox-cli --global
Examples:
$ workbox wizard
$ workbox wizard --injectManifest
$ workbox generateSW --watch
$ workbox injectManifest configs/workbox-dev-config.js
$ workbox copyLibraries build/
module.exports = {
globDirectory: 'build/web/',
globPatterns: ['**/*.{json,otf,ttf,js,wasm,png,html}'],
swSrc: 'sw.js',
swDest: 'build/web/sw.js',
maximumFileSizeToCacheInBytes: 10000000,
};
workbox-config.js
import { precacheAndRoute } from 'workbox-precaching';
precacheAndRoute(self.__WB_MANIFEST);
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.2.0/workbox-sw.js');
const { precacheAndRoute } = workbox.precaching;
precacheAndRoute(self.__WB_MANIFEST);
sw.js
flutter build web --pwa-strategy none
workbox injectManifest workbox-config.js
then
<script>
var scriptLoaded = false;
.....
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
....
</script>
index.html
<script>
var scriptLoaded = false;
.....
var serviceWorkerUrl = 'sw.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
....
</script>
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.2.0/workbox-sw.js');
const googleAnalytics = workbox.googleAnalytics;
const { precacheAndRoute } = workbox.precaching;
precacheAndRoute(self.__WB_MANIFEST);
googleAnalytics.initialize();
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.2.0/workbox-sw.js');
const googleAnalytics = workbox.googleAnalytics;
const { precacheAndRoute } = workbox.precaching;
const { ExpirationPlugin } = workbox.expiration;
const { CacheableResponsePlugin } = workbox.cacheableResponse;
const { registerRoute } = workbox.routing;
const { StaleWhileRevalidate, CacheFirst } = workbox.strategies;
// Cache the Google Fonts stylesheets with a stale-while-revalidate strategy.
registerRoute(
({ url }) => url.origin === 'https://fonts.googleapis.com',
new StaleWhileRevalidate({
cacheName: 'google-fonts-stylesheets',
})
);
// Cache the underlying font files with a cache-first strategy for 1 year.
registerRoute(
({ url }) => url.origin === 'https://fonts.gstatic.com',
new CacheFirst({
cacheName: 'google-fonts-webfonts',
plugins: [
new CacheableResponsePlugin({
statuses: [0, 200],
}),
new ExpirationPlugin({
maxAgeSeconds: 60 * 60 * 24 * 365,
maxEntries: 30,
}),
],
})
);
registerRoute(
({ url }) => url.origin === 'https://hacker-news.firebaseio.com',
new CacheFirst({
cacheName: 'stories',
plugins: [
new ExpirationPlugin({
maxEntries: 50,
maxAgeSeconds: 5 * 60, // 5 minutes
}),
new CacheableResponsePlugin({
statuses: [0, 200],
}),
],
})
);
<script type="module">
function createUIPrompt(opts) {
if (
confirm('New version of the application is downloaded, do you want to update? May take two reloads.')
) {
opts.onAccept();
}
}
import { Workbox } from 'https://storage.googleapis.com/workbox-cdn/releases/6.2.0/workbox-window.prod.mjs';
if ('serviceWorker' in navigator) {
const wb = new Workbox('/sw.js');
let registration;
const showSkipWaitingPrompt = (event) => {
const prompt = createUIPrompt({
onAccept: () => {
wb.addEventListener('controlling', (event) => {
window.location.reload();
});
wb.messageSkipWaiting();
},
onReject: () => {
prompt.dismiss();
},
});
};
wb.addEventListener('waiting', showSkipWaitingPrompt);
wb.register();
loadMainDartJs();
} else {
loadMainDartJs();
}
</script>
index.html
addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
sw.js
Navigator 2.0
Majid Hajian
mhadaily
Slides and link to source code
slides.com/mhadaily
SVG icons credited to undraw.co
By Majid Hajian
Progressive Web Apps are web applications that have been designed, so they are capable, reliable, and installable. These three pillars transform them into an experience that feels like a platform-specific application. The central core components that ensure that PWA works flawlessly are Service Workers. These components are a network proxy that takes control of a network request from the application and adds cache capabilities, background app sync, push notifications, and offline features. In this talk, I will review the PWA capabilities in Flutter and show you how you can extend these functionalities to have a better progressive web app building with Flutter in order to boost user experience in your application.
Majid Hajian is a passionate software developer with years of developing and architecting complex web and mobile applications. He is passionate about web platform especially flutter, IoT, PWAs, and performance.