cf_waiting_room 0.3.1 copy "cf_waiting_room: ^0.3.1" to clipboard
cf_waiting_room: ^0.3.1 copied to clipboard

Unofficial Flutter widget for integrating with Cloudflare Waiting Room — WebView-based queue gate with native overlay, session timeout, force re-queue flow, and custom UI builders.

cf_waiting_room #

Disclaimer: This package is an unofficial community library and is not affiliated with, endorsed by, or supported by Cloudflare, Inc. Cloudflare® is a registered trademark of Cloudflare, Inc.

An unofficial Flutter widget that gates your app behind a Cloudflare Waiting Room.


How it works — the user journey #

Imagine your app is a ticket-sale event backed by a Cloudflare Waiting Room.

User opens app
      │
      ▼
┌─────────────────────────────┐
│  Phase 1 – Live CF page     │  Full-screen WebView.
│  (WebView full-screen)      │  User sees the real CF queue page immediately.
└────────────┬────────────────┘
             │ CF confirms queue active
             ▼
┌─────────────────────────────┐
│  Phase 2 – Native overlay   │  Your brand UI replaces the raw CF page.
│  (overlay + 1×1 WebView)    │  The tiny invisible WebView keeps CF's JS
└────────────┬────────────────┘  running so the session cookie stays valid.
             │ CF redirects → pass page (title contains passKeyWord)
             ▼
┌─────────────────────────────┐
│  Phase 3 – Silent monitor   │  onQueueDone() fires — your app is shown.
│  (invisible 1×1 WebView)    │  A session timer continues ticking in the
└────────────┬────────────────┘  background to detect re-queue situations.
             │ sessionTimeout fires
             ▼
      WebView reloads silently
      ┌── queue active? ──▶ onNeedReQueue() → back to Phase 2
      └── still clear?  ──▶ onSessionTimeout() → timer restarts

Installation #

dependencies:
  cf_waiting_room: ^0.3.0

Quick start #

class _GatePageState extends State<_GatePage> {
  bool _queueDone = false;

  final _config = WaitingRoomConfig(
    isEnable: true,
    queueUrl: 'https://your-site.com/',
    queueKeyWord: ['waiting', 'queue'],
    passKeyWord: ['myapp'],           // substring of the real app page title
    sessionTimeoutMinutes: 25,        // post-pass monitoring interval
    isEnterprise: false,              // true if your CF zone is Enterprise
  );

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        if (_queueDone) YourAppContent(),
        CFWaitingRoomOverlayWidget(
          config: _config,
          onQueueDone: () => setState(() => _queueDone = true),
          onNeedReQueue: () => setState(() => _queueDone = false),
          onSessionTimeout: () => _showSessionExpiredBanner(),
        ),
      ],
    );
  }
}

Session timeout & CF plan tier #

sessionTimeoutMinutes starts counting after the queue is passed (Phase 3). When it fires the widget revokes the CF session and reloads silently to re-check whether the CF queue is active again.

WaitingRoomConfig(
  sessionTimeoutMinutes: 25,   // set to your CF waiting room session duration
  isEnterprise: false,         // default — use true for Enterprise zones
)

isEnterprise flag #

Cloudflare's session revocation via the Cf-Waiting-Room-Command: revoke HTTP header is an Enterprise-only feature.

Plan Session reset method
Free / Pro / Business Cookie jar + cache cleared locally in the WebView
Enterprise Cf-Waiting-Room-Command: revoke header — CF frees the slot server-side immediately

Non-enterprise grace period: CF automatically renews the __cfwaitingroom_* cookie expiry on every WebView request. The widget automatically adds 60 seconds to sessionTimeoutMinutes when isEnterprise is false, so the timer fires after the cookie has genuinely expired. Set sessionTimeoutMinutes equal to your CF session duration — the widget handles the extra 60 s internally.

// CF waiting room session = 20 min, non-enterprise
// Widget fires after 21 min (20 min + 60 s grace)
WaitingRoomConfig(
  sessionTimeoutMinutes: 20,
  isEnterprise: false,  // default
)

// CF waiting room session = 20 min, enterprise
// Widget fires after exactly 20 min + revoke header sent
WaitingRoomConfig(
  sessionTimeoutMinutes: 20,
  isEnterprise: true,
)

Mock mode — test the full flow without a live CF endpoint #

CFWaitingRoomOverlayWidget(
  config: _config,
  mockConfig: MockConfig(
    isEnable: true,
    waitDuration: Duration(seconds: 10), // auto-pass after 10 s
  ),
  onQueueDone: () => setState(() => _queueDone = true),
  onNeedReQueue: () => setState(() => _queueDone = false),
  onSessionTimeout: () => _showBanner('Session expired'),
)

Mock timeline (with sessionTimeoutMinutes: 1, waitDuration: 10s):

t Event
0 s Mock queue HTML loads → Phase 1 → Phase 2 overlay
10 s Auto-pass → onQueueDone() fires, your app appears (Phase 3)
25 s (10+15) Dialog: "Yes — re-queue"onNeedReQueue() + reset to Phase 1
"No — stay in app"onSessionTimeout() + timer restarts

Force re-queue (e.g. after a successful purchase) #

await CFWaitingRoomOverlayWidget.forceReQueue(
  context,
  config: _config,
  onConfirm: () => setState(() => _queueDone = false),
);

Customise the default overlay #

No full custom builder needed — pass widgets and styles directly:

CFWaitingRoomOverlayWidget(
  config: _config,
  onQueueDone: _onDone,
  overlayIcon: Image.asset('assets/logo.png', height: 64),
  loadingIcon: Image.asset('assets/spinner.gif', width: 56, height: 56),
  overlayBackgroundColor: const Color(0xFF0D1B2A),
  titleStyle: const TextStyle(color: Colors.amber, fontSize: 22),
  refreshMessageStyle: const TextStyle(color: Colors.white60, fontSize: 14),
)

Text labels driven from WaitingRoomConfig (Remote Config-friendly):

Config field Description
waitingTitle Overrides CF page <h1> — always shown
waitingRefreshMessage Body copy below the ETA
lastUpdatedPrefix Prefix before the last-updated timestamp

Locale — Accept-Language header #

Resolution order:

  1. CFWaitingRoomOverlayWidget.locale — widget-level Locale override
  2. WaitingRoomConfig.locale — Remote Config BCP-47 string, e.g. "zh-HK"
  3. Device system locale (PlatformDispatcher.instance.locale)
CFWaitingRoomOverlayWidget(
  config: _config,
  locale: const Locale('zh', 'HK'),
  onQueueDone: _onDone,
)

Custom UI builders #

Phase 2 waiting overlay #

CFWaitingRoomOverlayWidget(
  config: _config,
  onQueueDone: _onDone,
  waitingOverlayBuilder: (context, info) => MyWaitingScreen(
    title: info.title,
    eta: info.eta,
    lastUpdated: info.lastUpdated,
  ),
)

Force re-queue page #

CFWaitingRoomOverlayWidget(
  config: _config,
  onQueueDone: _onDone,
  reQueuePageBuilder: (context, onConfirm) =>
      MyReQueuePage(onConfirm: onConfirm),
)

Firebase Remote Config integration #

{
  "isEnable": true,
  "queueUrl": "https://your-site.com/",
  "queueKeyWord": ["waiting", "queue"],
  "passKeyWord": ["myapp"],
  "etaId": "waitTime",
  "lastUpdatedId": "last-updated",
  "sessionTimeoutMinutes": 25,
  "isEnterprise": false,
  "clearCookieOnStart": true,
  "locale": "zh-HK",
  "waitingTitle": "您正在排隊中,感謝耐心等候。",
  "waitingRefreshMessage": "本頁將自動更新,請勿關閉應用程式。",
  "lastUpdatedPrefix": "最後更新:",
  "reQueueDialogMessage": "恭喜您搶購成功!若想再次購買,請重新排隊。",
  "reQueueDialogBtnText": "確定並重新排隊"
}
final raw = remoteConfig.getString('waitingRoomConfig');
final config = raw.isNotEmpty
    ? WaitingRoomConfig.fromJson(jsonDecode(raw))
    : WaitingRoomConfig(isEnable: false);

訪特權 mode (skip-queue pass) #

Set clearCookieOnStart: false to preserve existing CF cookies so a returning user skips the queue if their session is still valid.


Platform support #

Platform Support
Android
iOS
Web ❌ (webview_flutter not supported on Web)
1
likes
150
points
257
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Unofficial Flutter widget for integrating with Cloudflare Waiting Room — WebView-based queue gate with native overlay, session timeout, force re-queue flow, and custom UI builders.

Repository (GitHub)
View/report issues

Topics

#webview #cloudflare #waiting-room #queue

License

MIT (license)

Dependencies

flutter, json_annotation, shared_preferences, webview_flutter

More

Packages that depend on cf_waiting_room