How AppFlowy Handles Deep Links for Login, Payment, and Invitation Callbacks
AppFlowy implements a plug-in-style deep link framework using an abstract DeepLinkHandler class, a singleton DeepLinkHandlerRegistry, and platform-specific listeners to route login, payment, and invitation URIs to specialized handlers.
AppFlowy leverages a modular deep link architecture to manage external callbacks for authentication, billing, and workspace invitations. According to the AppFlowy-IO/AppFlowy source code, the system delegates URI processing to discrete handlers registered during application startup, enabling clean separation between deep link detection and business logic.
Core Architecture Components
Three fundamental building blocks power AppFlowy's deep link handling:
-
DeepLinkHandler<T>: An abstract base class defined infrontend/appflowy_flutter/lib/startup/tasks/deeplink/deeplink_handler.dartthat declarescanHandle(Uri)andhandle(...)methods. Each concrete implementation decides whether it can process a URI and executes the associated logic. -
DeepLinkHandlerRegistry: A singleton registry that maintains a list of all active handlers. WhenprocessDeepLinkis invoked, the registry iterates through registered handlers and dispatches the URI to the first handler returningtruefromcanHandle. -
AppFlowyCloudDeepLink: The bootstrap component infrontend/appflowy_flutter/lib/startup/tasks/appflowy_cloud_task.dartthat initializes system-wide listeners and forwards incoming URIs to the registry. It also exposes aValueNotifierso the UI can react to loading states.
Deep Link Registration and Listening
Handler Registration During Startup
During application initialization, the AppFlowyCloudDeepLink constructor instantiates the registry and registers concrete handlers for each supported flow:
_deepLinkHandlerRegistry = DeepLinkHandlerRegistry.instance
..register(LoginDeepLinkHandler())
..register(PaymentDeepLinkHandler())
..register(InvitationDeepLinkHandler())
..register(ExpireLoginDeepLinkHandler())
..register(OpenAppDeepLinkHandler());
This registration pattern appears in frontend/appflowy_flutter/lib/startup/tasks/appflowy_cloud_task.dart, ensuring all handlers are available before the first URI arrives.
Platform-Specific URI Listening
The system adapts to platform conventions for intercepting external links:
- Mobile: Uses the
AppLinkslibrary to emit aUri?for every custom-scheme launch. - Windows: Registers the
appflowy-flutter://protocol viaregisterProtocolHandler(appflowyDeepLinkSchema).
The listener forwards captured URIs to _handleUri, which delegates processing to the registry:
await _deepLinkHandlerRegistry.processDeepLink(
uri: uri,
onStateChange: ...,
onResult: ...,
onError: ...,
);
Concrete Deep Link Handlers
Each specialized handler resides in frontend/appflowy_flutter/lib/startup/tasks/deeplink/ and implements specific business logic:
LoginDeepLinkHandler (login_deeplink_handler.dart)
Detects URIs where uri.fragment contains access_token (e.g., appflowy-flutter://login-callback#access_token=...). Upon handling, it dispatches a UserEventOauthSignIn to the backend, updates the loading state, and returns the signed-in UserProfilePB.
InvitationDeepLinkHandler (invitation_deeplink_handler.dart)
Matches hosts equal to invitation-callback with query parameters workspace_id and email. It extracts these values and pushes a WorkspaceNotifyValue to openWorkspaceNotifier, triggering the UI to open the invited workspace.
PaymentDeepLinkHandler (payment_deeplink_handler.dart)
Handles appflowy-flutter://payment-success?... URIs by reading the plan query parameter and notifying SubscriptionSuccessListenable that the purchase succeeded.
OpenAppDeepLinkHandler (open_app_deeplink_handler.dart)
Serves as a fallback for any appflowy-flutter:// URI that does not match specific handlers. It invokes runAppFlowy() to bring the application to the foreground without additional processing.
ExpireLoginDeepLinkHandler (expire_login_deeplink_handler.dart)
Processes token-expiry callbacks and surfaces error toasts to inform users of authentication failures.
State Management and UI Feedback
The framework propagates execution progress through typed callbacks:
DeepLinkState: An enum with valuesnone,loading,finish, anderroremitted viaonStateChange.- ValueNotifier:
AppFlowyCloudDeepLinkexposessubscribeDeepLinkLoadingState, which UI components use to display loading indicators during login flows (the only flow requiring explicit UI feedback).
Final results arrive through onResult (success or failure for recognized handlers) or onError (for malformed or unknown URIs).
Implementing Custom Deep Link Handlers
Developers can extend the system by subclassing DeepLinkHandler:
class MyCustomHandler extends DeepLinkHandler<void> {
@override
bool canHandle(Uri uri) => uri.host == 'my-custom';
@override
Future<FlowyResult<void, FlowyError>> handle({
required Uri uri,
required DeepLinkStateHandler onStateChange,
}) async {
// Custom logic here
return FlowyResult.success(null);
}
}
// Registration during startup
DeepLinkHandlerRegistry.instance.register(MyCustomHandler());
Summary
- AppFlowy uses a registry pattern (
DeepLinkHandlerRegistry) to route URIs to the first capable handler. - Five concrete handlers manage login, payment, invitation, token expiry, and generic app-opening deep links.
- Platform listeners in
AppFlowyCloudDeepLinkcapture URIs on mobile and Windows, forwarding them toprocessDeepLink. - State changes propagate through callbacks and
ValueNotifierinstances, enabling UI feedback during asynchronous operations like OAuth sign-in. - Unit tests in
frontend/appflowy_flutter/test/unit_test/deeplink/deeplink_test.dartverify handler routing and error handling. - The architecture supports extensibility via the abstract
DeepLinkHandlerbase class.
Frequently Asked Questions
How does AppFlowy distinguish between different types of deep links?
Each handler implements canHandle(Uri) with specific matching logic. For example, LoginDeepLinkHandler checks for access_token in the fragment, while PaymentDeepLinkHandler verifies the host equals payment-success. The registry iterates through registered handlers in order and selects the first returning true.
Where is the deep link listening logic implemented for Windows and mobile platforms?
The AppFlowyCloudDeepLink class in frontend/appflowy_flutter/lib/startup/tasks/appflowy_cloud_task.dart initializes platform-specific listeners. It uses the AppLinks library for mobile and url_protocol with registerProtocolHandler(appflowyDeepLinkSchema) for Windows to capture appflowy-flutter:// scheme launches.
Can I add support for custom deep link schemes in AppFlowy?
Yes. Create a class extending DeepLinkHandler<T> in frontend/appflowy_flutter/lib/startup/tasks/deeplink/, implement canHandle to match your URI pattern, and register the instance via DeepLinkHandlerRegistry.instance.register() during application startup.
How does the UI know when a login deep link is being processed?
The AppFlowyCloudDeepLink class exposes subscribeDeepLinkLoadingState, a ValueNotifier that emits DeepLinkResult objects containing the current state (loading, finish, or error). UI widgets listen to this notifier to display progress indicators or error messages during the OAuth handshake.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →