Flutter บน iOS, Android และเว็บ
ไคลเอนต์ Dart ตัวเดียว แชนเนลส่วนตัว เชื่อมต่อใหม่ และพุชเมื่อแอปปิดอยู่
Flutter สร้างแอปเดียวสำหรับ iOS, Android, เว็บ และเดสก์ท็อป โค้ดเรียลไทม์ก็ควรเป็นชุดเดียวด้วย ปลั๊กอิน Pusher Flutter ทางการเชื่อมต่อกับโฮสต์ของ pusher.com เสมอ จึงควรใช้ dart_pusher_channels แทน ซึ่งเป็นไคลเอนต์โปรโตคอล Pusher ที่เขียนด้วย Dart ล้วน ตั้งค่าโฮสต์ได้ และรันได้ทุกที่ที่ Flutter รัน
สิ่งที่ผู้ใช้ได้รับ
- หน้าคำสั่งซื้อ แชต และคะแนนที่อัปเดตในที่ โดยไม่ต้องดึงลงเพื่อรีเฟรช
- การเชื่อมต่อที่กลับมาเองหลังผ่านอุโมงค์ ขึ้นลิฟต์ หรือสลับจาก Wi-Fi เป็นเน็ตมือถือ
- ทำงานเหมือนกันทั้งบนมือถือและในเบราว์เซอร์
ฟีเจอร์ที่ใช้
- dart_pusher_channels ไคลเอนต์ตัวเดียวสำหรับทั้งแอป
- แชนเนลส่วนตัว (
private-) ที่แบ็กเอนด์ของคุณยืนยันสิทธิ์ด้วยโทเคนของผู้ใช้ที่ล็อกอินอยู่ - อีเวนต์ฝั่งไคลเอนต์บนแชนเนลส่วนตัวและ presence สำหรับสถานะกำลังพิมพ์และสัญญาณเล็ก ๆ อื่น ๆ
- Hibira Push สำหรับช่วงที่แอปอยู่เบื้องหลังหรือปิดอยู่
ขั้นที่ 1: ติดตั้ง
flutter pub add dart_pusher_channelsขั้นที่ 2: ไคลเอนต์ตัวเดียวสำหรับทั้งแอป
// lib/realtime.dart
import 'package:dart_pusher_channels/dart_pusher_channels.dart';
final realtime = PusherChannelsClient.websocket(
options: PusherChannelsOptions.fromHost(
scheme: 'wss',
host: 'ws.hibira.io',
port: 443,
key: const String.fromEnvironment('HIBIRA_KEY'),
),
connectionErrorHandler: (error, trace, refresh) => refresh(),
);ส่ง key ตอน build: flutter run --dart-define=HIBIRA_KEY=YOUR_APP_KEY ห้ามใส่ app secret ไว้ในแอปเด็ดขาด แชนเนลส่วนตัวลงนามโดยแบ็กเอนด์ของคุณ
ขั้นที่ 3: ติดตามคำสั่งซื้อหนึ่งรายการบนหน้าจอ
// lib/order_screen.dart
import 'dart:async';
import 'package:dart_pusher_channels/dart_pusher_channels.dart';
import 'package:flutter/material.dart';
import 'realtime.dart';
class OrderScreen extends StatefulWidget {
const OrderScreen({super.key, required this.orderId, required this.token});
final String orderId;
final String token;
State<OrderScreen> createState() => _OrderScreenState();
}
class _OrderScreenState extends State<OrderScreen> {
late final PrivateChannel channel;
final subs = <StreamSubscription>[];
String status = '…';
void initState() {
super.initState();
channel = realtime.privateChannel(
'private-order-${widget.orderId}',
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate.forPrivateChannel(
authorizationEndpoint: Uri.parse('https://api.example.com/pusher/auth'),
headers: {'Authorization': 'Bearer ${widget.token}'},
),
);
subs.add(channel.bind('status').listen((event) {
setState(() => status = event.tryGetDataAsMap()?['status'] ?? status);
}));
subs.add(realtime.onConnectionEstablished.listen((_) => channel.subscribeIfNotUnsubscribed()));
channel.subscribeIfNotUnsubscribed();
}
void dispose() {
for (final s in subs) {
s.cancel();
}
channel.unsubscribe();
super.dispose();
}
Widget build(BuildContext context) => Text(status);
}การสมัครรับใหม่ใน onConnectionEstablished ทำให้แชนเนลกลับมาทุกครั้งหลังเชื่อมต่อใหม่ endpoint ยืนยันสิทธิ์ของแบ็กเอนด์จะได้รับ socket_id และ channel_name ตรวจโทเคน แล้วส่งลายเซ็นที่สร้างจากไลบรารีฝั่งเซิร์ฟเวอร์ที่เข้ากันได้กับ Pusher กลับไป
ขั้นที่ 4: เชื่อมต่อเฉพาะตอนที่แอปเปิดอยู่
class _AppState extends State<App> with WidgetsBindingObserver {
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
realtime.connect();
}
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.paused) realtime.disconnect();
if (state == AppLifecycleState.resumed) realtime.connect();
}
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
Widget build(BuildContext context) => const MaterialApp(home: HomeScreen());
}iOS และ Android หยุดเครือข่ายของแอปที่อยู่เบื้องหลังอยู่แล้ว จึงควรตัดการเชื่อมต่อเมื่อ paused และเชื่อมต่อใหม่เมื่อ resumed เรื่องที่ผู้ใช้ต้องรู้ขณะที่แอปปิดอยู่ควรส่งเป็นการแจ้งเตือนแบบพุช ดูการแจ้งเตือนแบบพุชที่ดึงผู้ใช้กลับมา และคู่มือพุช ซึ่งอธิบาย SDK ของ Android และ iOS ที่แอปโฮสต์ของ Flutter เรียกใช้ได้
เคล็ดลับ
- โหลดสิ่งที่หน้าจอต้องใช้จาก API ก่อน แล้วให้อีเวนต์อัปเดตต่อ สถานะที่ต้องมีตั้งแต่เปิดหน้า ให้เผยแพร่ไปที่แชนเนล
cache- - อีเวนต์ฝั่งไคลเอนต์ (
channel.trigger(eventName: 'client-typing', data: {...})) ใช้ได้บนแชนเนลส่วนตัวและ presence เมื่อเปิด Allow client events ในการตั้งค่าแอป - เมื่อเข้าสู่ระบบแล้ว คู่มือ Flutter จะมีการตั้งค่าไคลเอนต์ที่กรอกค่าของแอปคุณไว้ให้