├── README.md ├── templates ├── index.html └── layout.html ├── static ├── images │ ├── pwa-dark.png │ ├── pwa-light.png │ ├── pwa-dark@2x.png │ ├── pwa-light@2x.png │ ├── luca-bravo-272609.png │ ├── william-iven-8514.jpg │ ├── harprit-bola-239056.png │ ├── kevin-bhagat-461952.jpg │ ├── android-launchericon-144-144.png │ ├── android-launchericon-192-192.png │ ├── android-launchericon-48-48.png │ ├── android-launchericon-512-512.png │ ├── android-launchericon-72-72.png │ ├── android-launchericon-96-96.png │ └── marta-de-la-concepcion-97951.jpg ├── styles.css ├── offline.html ├── manifest.json ├── app.js └── service-worker.js └── flask-pwa.py /README.md: -------------------------------------------------------------------------------- 1 | # PWA-Flask-Python-Template 2 | PWA with python Flask 3 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html' %} 2 | {% block body %} 3 | 4 | 5 | 6 | {% endblock %} -------------------------------------------------------------------------------- /static/images/pwa-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/PWA-Flask-Python-Template/HEAD/static/images/pwa-dark.png -------------------------------------------------------------------------------- /static/images/pwa-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/PWA-Flask-Python-Template/HEAD/static/images/pwa-light.png -------------------------------------------------------------------------------- /static/images/pwa-dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/PWA-Flask-Python-Template/HEAD/static/images/pwa-dark@2x.png -------------------------------------------------------------------------------- /static/images/pwa-light@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/PWA-Flask-Python-Template/HEAD/static/images/pwa-light@2x.png -------------------------------------------------------------------------------- /static/images/luca-bravo-272609.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/PWA-Flask-Python-Template/HEAD/static/images/luca-bravo-272609.png -------------------------------------------------------------------------------- /static/images/william-iven-8514.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/PWA-Flask-Python-Template/HEAD/static/images/william-iven-8514.jpg -------------------------------------------------------------------------------- /static/images/harprit-bola-239056.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/PWA-Flask-Python-Template/HEAD/static/images/harprit-bola-239056.png -------------------------------------------------------------------------------- /static/images/kevin-bhagat-461952.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/PWA-Flask-Python-Template/HEAD/static/images/kevin-bhagat-461952.jpg -------------------------------------------------------------------------------- /static/images/android-launchericon-144-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/PWA-Flask-Python-Template/HEAD/static/images/android-launchericon-144-144.png -------------------------------------------------------------------------------- /static/images/android-launchericon-192-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/PWA-Flask-Python-Template/HEAD/static/images/android-launchericon-192-192.png -------------------------------------------------------------------------------- /static/images/android-launchericon-48-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/PWA-Flask-Python-Template/HEAD/static/images/android-launchericon-48-48.png -------------------------------------------------------------------------------- /static/images/android-launchericon-512-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/PWA-Flask-Python-Template/HEAD/static/images/android-launchericon-512-512.png -------------------------------------------------------------------------------- /static/images/android-launchericon-72-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/PWA-Flask-Python-Template/HEAD/static/images/android-launchericon-72-72.png -------------------------------------------------------------------------------- /static/images/android-launchericon-96-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/PWA-Flask-Python-Template/HEAD/static/images/android-launchericon-96-96.png -------------------------------------------------------------------------------- /static/images/marta-de-la-concepcion-97951.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soumilshah1995/PWA-Flask-Python-Template/HEAD/static/images/marta-de-la-concepcion-97951.jpg -------------------------------------------------------------------------------- /static/styles.css: -------------------------------------------------------------------------------- 1 | 2 | h1{ 3 | color: red; 4 | text-align: center; 5 | } 6 | 7 | p{ 8 | color: black; 9 | text-align: center; 10 | } 11 | 12 | .jumbotron{ 13 | height: 60vh; 14 | } 15 | 16 | li{ 17 | color: white; 18 | } 19 | -------------------------------------------------------------------------------- /static/offline.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Offline! 6 | 7 | 8 |

It seems you might be offline!

9 |

Go back to home page

10 | 11 | -------------------------------------------------------------------------------- /flask-pwa.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify, render_template 2 | 3 | app = Flask(__name__) 4 | 5 | 6 | @app.route('/') 7 | def home(): 8 | return render_template('index.html') 9 | 10 | 11 | @app.route('/offline.html') 12 | def offline(): 13 | return app.send_static_file('offline.html') 14 | 15 | 16 | @app.route('/service-worker.js') 17 | def sw(): 18 | return app.send_static_file('service-worker.js') 19 | 20 | 21 | if __name__ == '__main__': 22 | app.run(debug=True, host="0.0.0.0", port=8080) 23 | -------------------------------------------------------------------------------- /static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Demo Flask Progressive Web App (PWA)", 3 | "short_name": "Flask PWA", 4 | "theme_color": "#34A5DA", 5 | "background_color": "#FFFFFF", 6 | "icons": [ 7 | { 8 | "src": "/static/images/android-launchericon-48-48.png", 9 | "type": "image/png", 10 | "sizes": "48x48" 11 | }, 12 | { 13 | "src": "/static/images/android-launchericon-96-96.png", 14 | "type": "image/png", 15 | "sizes": "96x96" 16 | }, 17 | { 18 | "src": "/static/images/android-launchericon-144-144.png", 19 | "type": "image/png", 20 | "sizes": "144x144" 21 | }, 22 | { 23 | "src": "/static/images/android-launchericon-192-192.png", 24 | "type": "image/png", 25 | "sizes": "192x192" 26 | }, 27 | { 28 | "src": "/static/images/android-launchericon-512-512.png", 29 | "type": "image/png", 30 | "sizes": "512x512" 31 | } 32 | ], 33 | "start_url": "/", 34 | "display": "standalone", 35 | "orientation": "portrait" 36 | } -------------------------------------------------------------------------------- /static/app.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | // Register Service Worker 8 | if ('serviceWorker' in navigator) { 9 | navigator.serviceWorker 10 | .register('/service-worker.js') 11 | .then(function(registration) { 12 | console.log('Service Worker Registered'); 13 | return registration; 14 | }) 15 | .catch(function(err) { 16 | console.error('Unable to register service worker.', err); 17 | }); 18 | } 19 | 20 | 21 | 22 | 23 | let deferredPrompt; 24 | const addBtn = document.querySelector('.add-button'); 25 | addBtn.style.display = 'none'; 26 | 27 | 28 | 29 | window.addEventListener('beforeinstallprompt', (e) => { 30 | e.preventDefault(); 31 | deferredPrompt = e; 32 | addBtn.style.display = 'block'; 33 | addBtn.addEventListener('click', (e) => { 34 | addBtn.style.display = 'none'; 35 | deferredPrompt.prompt(); 36 | deferredPrompt.userChoice.then((choiceResult) => { 37 | if (choiceResult.outcome === 'accepted') { 38 | 39 | 40 | console.log('User accepted the A2HS prompt'); 41 | 42 | 43 | } else { 44 | 45 | console.log('User dismissed the A2HS prompt'); 46 | 47 | } 48 | deferredPrompt = null; 49 | }); 50 | }); 51 | }); 52 | 53 | 54 | 55 | 56 | window.addEventListener('online', function(e) { 57 | console.log("You are online"); 58 | }, false); 59 | 60 | 61 | -------------------------------------------------------------------------------- /static/service-worker.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | const cacheName = 'flask-PWA-v3'; 7 | const filesToCache = [ 8 | '/', 9 | '/static/app.js', 10 | '/static/styles.css', 11 | '/static/images/pwa-light.png' 12 | ]; 13 | 14 | 15 | 16 | 17 | 18 | self.addEventListener('install', function(e) { 19 | console.log('[ServiceWorker] Install'); 20 | e.waitUntil( 21 | caches.open(cacheName).then(function(cache) { 22 | console.log('[ServiceWorker] Caching app shell'); 23 | return cache.addAll(filesToCache); 24 | }) 25 | ); 26 | }); 27 | 28 | 29 | 30 | 31 | 32 | 33 | self.addEventListener('activate', function(e) { 34 | console.log('[ServiceWorker] Activate'); 35 | e.waitUntil( 36 | caches.keys().then(function(keyList) { 37 | return Promise.all(keyList.map(function(key) { 38 | if (key !== cacheName) { 39 | console.log('[ServiceWorker] Removing old cache', key); 40 | return caches.delete(key); 41 | } 42 | })); 43 | }) 44 | ); 45 | return self.clients.claim(); 46 | }); 47 | 48 | 49 | 50 | 51 | 52 | 53 | self.addEventListener('fetch', function(e) { 54 | console.log('[ServiceWorker] Fetch', e.request.url); 55 | e.respondWith( 56 | caches.match(e.request).then(function(response) { 57 | return response || fetch(e.request).catch(error => { 58 | console.log('Fetch failed; returning offline page instead.', error); 59 | let url = e.request.url; 60 | let extension = url.split('.').pop(); 61 | 62 | if (extension === 'jpg' || extension === 'png') { 63 | 64 | const FALLBACK_IMAGE = ` 65 | 66 | 67 | 68 | 69 | `; 70 | 71 | return Promise.resolve(new Response(FALLBACK_IMAGE, { 72 | headers: { 73 | 'Content-Type': 'image/svg+xml' 74 | } 75 | })); 76 | } 77 | 78 | return caches.match('offline.html'); 79 | }); 80 | }) 81 | ); 82 | }); -------------------------------------------------------------------------------- /templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Flask PWA 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 32 | 33 |
34 |

Flask Progressive Web App (PWA)

35 |

The next BIG thing for the mobile web!

36 | 37 |
38 | 39 |
40 | 41 |
42 |
43 | {% block body %} 44 | {% endblock %} 45 |
46 |
47 | 48 | 49 | 50 | 51 | --------------------------------------------------------------------------------