การสร้าง Progressive Web Apps (PWA) ด้วย Flutter
ในบทความก่อนหน้า เราได้เรียนรู้เกี่ยวกับการใช้ Push Notifications ใน Flutter ทั้งผ่าน Firebase Cloud Messaging (FCM) และ Local Notifications คราวนี้ เราจะมาขยายขอบเขตการใช้งาน Flutter ไปอีกขั้นด้วยการสร้าง Progressive Web Apps (PWAs) ซึ่งจะทำให้แอป Flutter ของคุณสามารถทำงานได้บนเว็บเบราว์เซอร์ พร้อมทั้งมอบประสบการณ์การใช้งานที่ใกล้เคียงกับแอปพลิเคชัน Native บนมือถือ
Progressive Web Apps (PWAs) คือเว็บไซต์ที่ถูกสร้างขึ้นด้วยเทคโนโลยีเว็บมาตรฐาน (HTML, CSS, JavaScript) แต่ได้รับการปรับปรุงให้มีคุณสมบัติที่ทำให้ผู้ใช้ได้รับประสบการณ์ที่คล้ายกับการใช้งานแอปพลิเคชัน Native บนมือถือ เช่น การติดตั้งลงบนหน้าจอหลัก, การทำงานแบบออฟไลน์, และการรองรับ Push Notifications
ข้อดีของการสร้าง PWA จากแอป Flutter คือ:
- Reusability: ใช้ Codebase เดียวกันสำหรับทั้งแอปมือถือ (Android/iOS) และเว็บแอปพลิเคชัน
- Cross-Platform Compatibility: ทำงานได้บนเบราว์เซอร์ที่รองรับ PWA บนหลากหลายระบบปฏิบัติการ
- Installability: ผู้ใช้สามารถติดตั้ง PWA ลงบนหน้าจอหลักได้เหมือนแอป Native
- Offline Support: ทำงานได้แม้ไม่มีการเชื่อมต่ออินเทอร์เน็ต (จำกัดตามการออกแบบ)
- SEO Friendly: สามารถ Index ได้โดย Search Engine ทำให้ค้นหาได้ง่าย
PWA อาศัยเทคโนโลยีหลักสองอย่างคือ Service Worker และ Manifest เราจะมาเจาะลึกในรายละเอียดกัน
Service Worker
Service Worker คือ JavaScript file ที่ทำงานอยู่เบื้องหลัง (background) ในเบราว์เซอร์ ทำหน้าที่เป็น Proxy ระหว่างเว็บแอปพลิเคชันกับ Network และ Cache มีหน้าที่หลักในการจัดการ Cache, รองรับการทำงานแบบออฟไลน์, และจัดการ Push Notifications
เหตุผลเชิงออกแบบ: Service Worker ช่วยให้ PWA สามารถทำงานได้แม้ไม่มีการเชื่อมต่ออินเทอร์เน็ต โดยการ Cache Assets ที่จำเป็น (HTML, CSS, JavaScript, Images) และให้บริการจาก Cache เมื่อ Network ไม่พร้อมใช้งาน นอกจากนี้ยังช่วยปรับปรุง Performance โดยการลด Latency ในการโหลด Assets
Trade-offs: การใช้งาน Service Worker ต้องมีการจัดการ Cache อย่างรอบคอบ เพื่อให้แน่ใจว่าผู้ใช้ได้รับ Assets ที่เป็นเวอร์ชันล่าสุด หากมีการเปลี่ยนแปลง Code หรือ Assets จะต้องมีการ Update Cache ให้ถูกต้อง
แนวปฏิบัติที่ดี:
- ใช้ Cache Strategies ที่เหมาะสม เช่น Cache First, Network First, Stale-While-Revalidate
- ใช้ Versioning สำหรับ Cache เพื่อให้ง่ายต่อการ Update
- ใช้ Tools ที่ช่วยในการ Generate Service Worker เช่น Workbox
ใน Flutter การ Generate Service Worker จะทำโดยอัตโนมัติเมื่อ Build Web App แต่เราสามารถปรับแต่งการทำงานของ Service Worker ได้โดยการแก้ไขไฟล์ service_worker.js ที่อยู่ใน web directory
ตัวอย่าง:
// service_worker.js
const CACHE_NAME = 'my-pwa-cache-v1';
const urlsToCache = [
'/',
'/index.html',
'/main.dart.js',
'/flutter_service_worker.js',
'/icons/Icon-192.png',
'/icons/Icon-512.png',
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
// IMPORTANT: Clone the request. A request is a stream
// and because we've already read it once, we need to clone it
var fetchRequest = event.request.clone();
return fetch(fetchRequest).then(
function(response) {
// Check if we received a valid response
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// IMPORTANT: Clone the response. A response is a stream
// and because we've already read it once, we need to clone the cache object.
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
});
self.addEventListener('activate', function(event) {
var cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});
โค้ดตัวอย่างนี้แสดง Service Worker ที่ทำการ Cache Assets ที่กำหนดไว้ใน urlsToCache เมื่อมีการ Install และให้บริการจาก Cache เมื่อมีการ Fetch Assets
Manifest
Manifest คือ JSON file ที่ให้ข้อมูลเกี่ยวกับ PWA เช่น ชื่อ, คำอธิบาย, ไอคอน, สีพื้นหลัง, และ Start URL Manifest ช่วยให้เบราว์เซอร์สามารถติดตั้ง PWA ลงบนหน้าจอหลักได้ และกำหนดลักษณะการแสดงผลของ PWA เมื่อเปิดจากหน้าจอหลัก
เหตุผลเชิงออกแบบ: Manifest ช่วยให้ PWA มีลักษณะที่คล้ายกับแอป Native ทำให้ผู้ใช้ได้รับประสบการณ์ที่ต่อเนื่องและเป็นมิตร
Trade-offs: การกำหนดค่า Manifest ที่ไม่ถูกต้องอาจทำให้ PWA แสดงผลไม่ถูกต้อง หรือไม่สามารถติดตั้งลงบนหน้าจอหลักได้
แนวปฏิบัติที่ดี:
- กำหนดค่า Manifest ให้ครบถ้วนและถูกต้อง
- ใช้ไอคอนที่มีความละเอียดหลากหลาย เพื่อให้ PWA แสดงผลได้ดีบนอุปกรณ์ที่แตกต่างกัน
- ทดสอบการติดตั้ง PWA บนอุปกรณ์และเบราว์เซอร์ที่หลากหลาย
ใน Flutter Manifest จะถูก Generate โดยอัตโนมัติเมื่อ Build Web App โดยใช้ข้อมูลจาก pubspec.yaml และไฟล์ index.html เราสามารถปรับแต่ง Manifest ได้โดยการแก้ไขไฟล์ manifest.json ที่อยู่ใน web directory
ตัวอย่าง:
// manifest.json
{
"name": "My Flutter PWA",
"short_name": "Flutter PWA",
"start_url": ".",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#007bff",
"description": "A Flutter Progressive Web App",
"orientation": "portrait",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
โค้ดตัวอย่างนี้แสดง Manifest ที่กำหนดชื่อ, คำอธิบาย, ไอคอน, และสีพื้นหลังของ PWA
การสร้าง PWA จาก Flutter App
ขั้นตอนการสร้าง PWA จาก Flutter App มีดังนี้:
- สร้าง Flutter Project:
flutter create my_pwa_app - Enable Web Support:
flutter config --enable-web - Build Web App:
flutter build web - Deploy Web App: Deploy ไฟล์ที่อยู่ใน
build/webdirectory ไปยัง Web Server
เมื่อ Deploy Web App แล้ว ผู้ใช้สามารถเข้าถึง PWA ผ่านเบราว์เซอร์ และติดตั้งลงบนหน้าจอหลักได้
สรุป
ในบทความนี้ เราได้เรียนรู้เกี่ยวกับการสร้าง Progressive Web Apps (PWAs) ด้วย Flutter โดยเน้นไปที่ Service Worker และ Manifest ซึ่งเป็นเทคโนโลยีหลักที่ทำให้ PWA สามารถทำงานได้บนเว็บเบราว์เซอร์ พร้อมทั้งมอบประสบการณ์การใช้งานที่ใกล้เคียงกับแอปพลิเคชัน Native บนมือถือ การสร้าง PWA จาก Flutter App ช่วยให้เราสามารถ Reuse Codebase เดียวกันสำหรับทั้งแอปมือถือและเว็บแอปพลิเคชัน ทำให้ประหยัดเวลาและทรัพยากรในการพัฒนา
ในบทความถัดไป เราจะมาเรียนรู้เกี่ยวกับการใช้ WebSockets ใน Flutter เพื่อสร้างแอปพลิเคชัน Real-time