iOS、Android 與網頁上的 Flutter
一個 Dart 用戶端:私有頻道、自動重連,App 關閉時改用推播。
Flutter 用一份程式碼做出 iOS、Android、網頁與桌面應用程式,即時功能的程式碼也該是一份。官方的 Pusher Flutter 外掛一律連到 pusher.com 的主機,所以請改用 dart_pusher_channels:一個純 Dart 的 Pusher 協定用戶端,可以設定 host,而且 Flutter 能跑的地方它都能跑。
使用者得到什麼
- 訂單、聊天與比分畫面就地更新,不必下拉重新整理。
- 經過隧道、搭電梯,或從 Wi-Fi 切到行動網路後,連線會自己回來。
- 手機上和瀏覽器裡的行為一樣。
用到的功能
- dart_pusher_channels,整個 App 共用一個用戶端。
- 私有頻道(
private-),由你的後端用已登入使用者的 token 授權。 - 私有與 presence 頻道上的用戶端事件,用於輸入中提示與其他小訊號。
- App 在背景或已關閉時,改用 Hibira Push。
步驟 1:安裝
flutter pub add dart_pusher_channels步驟 2:整個 App 共用一個用戶端
// 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:flutter run --dart-define=HIBIRA_KEY=YOUR_APP_KEY。App secret 絕不放進 App;私有頻道由你的後端簽署。
步驟 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 時重新訂閱,每次重新連線後頻道都會回來。你的後端授權端點會收到 socket_id 與 channel_name,檢查 token 後,用任何相容 Pusher 的伺服器函式庫回傳簽章。
步驟 4:App 開著時才連線
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 本來就會暫停背景 App 的網路,所以在 paused 時斷線,resumed 時再連上。App 關閉時使用者一定要知道的事,請用推播通知送出:請看把使用者帶回來的推播通知與推播指南,其中介紹了你的 Flutter 宿主 App 可以呼叫的 Android 與 iOS SDK。
小提醒
- 先從你的 API 載入畫面需要的資料,再讓事件更新它;打開時就必須存在的狀態,請發布到
cache-頻道。 - 在應用程式設定中開啟 Allow client events 後,用戶端事件(
channel.trigger(eventName: 'client-typing', data: {...}))就能在私有與 presence 頻道上使用。 - 登入後,Flutter 指南會用你的應用程式數值填好用戶端設定。