HosteDay Flutter
A Flutter and Dart SDK for connecting applications to HosteDay APIs, authentication, and Pusher-compatible real-time services.
hosteday_flutter provides a single SDK entry point for:
- Firebase-inspired authentication and session management.
- Automatic access-token handling for protected API requests.
- User profile management and password reset flows.
- Custom HTTP requests through
GET,POST,PUT,PATCH, andDELETE. - Public, private, presence, and private encrypted Realtime channels.
- Automatic authenticated channel authorization after sign-in.
- Configurable API endpoints and project-level request protection.

Features
-
Global SDK initialization through
HosteDay.initializeApp(...). -
Firebase-style authentication API through
HosteDay.auth. -
Persistent or temporary user sessions through
HosteDayAuthStorage. -
Automatic session restoration during application startup.
-
Automatic access-token usage for requests with
withAuth: true. -
Automatic token usage for private, presence, and private encrypted Realtime channels.
-
Authentication state streams:
authStateChanges()idTokenChanges()userChanges()
-
Built-in authentication operations:
- Sign in
- Register
- Sign out
- Password reset
- Profile reload
- Profile update
- Email verification request
-
Structured errors through:
HosteDayExceptionHosteDayAuthException
-
Realtime defaults:
- Scheme:
wss - Port:
443
- Scheme:
-
Complete WebSocket endpoint through
HosteDay.config.realtimeUrl.
Installation
Add the package to your Flutter project:
flutter pub add hosteday_flutter
Then import it:
import 'package:hosteday_flutter/hosteday_flutter.dart';
Quick Start
Initialize HosteDay once before running the application:
import 'package:flutter/material.dart';
import 'package:hosteday_flutter/hosteday_flutter.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await HosteDay.initializeApp(
options: const <String, Object?>{
HosteDayOptionKeys.projectDomain: 'your-project.hosteday.com',
// Required only when using Realtime.
HosteDayOptionKeys.realtimeHost: 'ws3.hosteday.com',
HosteDayOptionKeys.pusherKey: 'YOUR_PUSHER_KEY',
},
);
runApp(const App());
}
project_domain is required.
realtime_host is optional when Realtime uses the same domain as the API project. If omitted,
HosteDay uses the host from project_domain or api_base_url.
You do not need to pass these values unless your server requires different settings:
realtime_scheme = wss
realtime_port = 443
Core API
The official SDK entry point is:
HosteDay
Use these accessors after initialization:
HosteDay.instance
HosteDay.client
HosteDay.config
HosteDay.auth
Example:
final client = HosteDay.client;
final auth = HosteDay.auth;
final config = HosteDay.config;
Hostedayremains available as a deprecated compatibility alias. UseHosteDayin all new projects.
Authentication
HosteDayAuth provides a Firebase-inspired authentication API.
HosteDay.auth
Available accessors:
HosteDay.auth.currentUser
HosteDay.auth.currentSession
HosteDay.auth.getAccessToken
()
Available state streams:
HosteDay.auth.authStateChanges
()
HosteDay.auth.idTokenChanges
()
HosteDay.auth.userChanges
()
Authentication Gate
Use authStateChanges() to switch between signed-in and signed-out screens.
class AuthGate extends StatelessWidget {
const AuthGate({super.key});
@override
Widget build(BuildContext context) {
return StreamBuilder<HosteDayUser?>(
stream: HosteDay.auth.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
final user = snapshot.data;
if (user == null) {
return const LoginPage();
}
return HomePage(user: user);
},
);
}
}
The stream emits the current user immediately, then emits future sign-in and sign-out changes.
Sign In
final credential = await
HosteDay.auth.signInWithEmailAndPassword
(
email: 'user@example.com',
password: 'password',
);
final user = credential.user;
final session = credential.session;
print(user.id);
print(user.email);
print
(
session
.
accessToken
);
After successful sign-in, HosteDay automatically:
- Stores the authenticated session.
- Stores the access token.
- Updates
currentUser. - Emits authentication state changes.
- Uses the new token for protected requests.
- Uses the new token when authorizing private Realtime channels.
Register
final credential = await
HosteDay.auth.createUserWithEmailAndPassword
(
email: 'new-user@example.com',
password: 'password',
additionalData: <String, dynamic>{
'name': 'Mustafa',
},
);
print(credential.user.displayName);
additionalData can include backend-specific fields such as:
additionalData: <
String, dynamic>{
'name': 'Mustafa',
'phone': '+964...',
'tenant_id': 1,
}
The SDK automatically adds:
email
password
password_confirmation
Current User
final user = HosteDay.auth.currentUser;
if (
user == null) {
print('No user is signed in.');
} else {
print(user.id);
print(user.displayName);
print(user.email);
print(user.emailVerified);
print(user.photoUrl);
}
Reload User Data
Reload the current user from the configured user endpoint:
final user = await
HosteDay.auth.reload
();
print
(
user.displayName);
print(user.emailVerified);
The default endpoint is:
GET /api/user
Update Profile
final user = await
HosteDay.auth.updateProfile
(<String, dynamic>{
'name': 'Mustafa Max',
'email': 'mustafa@example.com',
},
);
print(user.displayName);
The default endpoint is:
PUT /api/user
The supported fields depend on your Laravel backend.
Email Verification
Request an email verification message for the signed-in user:
await
HosteDay.auth.sendEmailVerification
();
The default endpoint is:
POST /api/email/verify
Password Reset
Send Password Reset Email
await
HosteDay.auth.sendPasswordResetEmail
(
email
:
'
user@example.com
'
,
);
The default endpoint is:
POST /api/auth/forgot-password
Confirm Password Reset
await
HosteDay.auth.confirmPasswordReset
(
email: 'user@example.com',
token: 'RESET_TOKEN',
newPassword
:
'
new-password
'
,
);
The default endpoint is:
POST /api/auth/reset-password
Sign Out
await
HosteDay.auth.signOut
();
When signing out, HosteDay:
- Attempts to call the configured logout endpoint.
- Removes the local session and access token.
- Sets
currentUsertonull. - Emits
nullthrough authentication streams. - Disconnects Realtime to prevent private subscriptions from remaining active.
The default endpoint is:
POST /api/logout
The local session is removed even if the remote logout endpoint fails.
Session Storage
By default, HosteDay uses MemoryHosteDayAuthStorage.
This is useful for tests and quick examples, but the session will be lost when the application closes.
For production applications, provide a persistent secure implementation of HosteDayAuthStorage.
Example using flutter_secure_storage in your Flutter application:
flutter pub add flutter_secure_storage
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hosteday_flutter/hosteday_flutter.dart';
class SecureHosteDayAuthStorage implements HosteDayAuthStorage {
const SecureHosteDayAuthStorage(this._storage);
final FlutterSecureStorage _storage;
@override
Future<String?> read(String key) {
return _storage.read(key: key);
}
@override
Future<void> write(String key, String value) {
return _storage.write(
key: key,
value: value,
);
}
@override
Future<void> delete(String key) {
return _storage.delete(key: key);
}
}
Use it during initialization:
await
HosteDay.initializeApp
(
options: const <String, Object?>{
HosteDayOptionKeys.projectDomain: 'your-project.hosteday.com',
},
authStorage: SecureHosteDayAuthStorage(
FlutterSecureStorage(),
),
);
Never store production access tokens in plain text storage.
Protected API Requests
After sign-in, use withAuth: true for protected requests.
final response = await
HosteDay.client.get
('/api/posts
'
,withAuth: true,
);
print(response);
The active access token is automatically added as:
Authorization: Bearer YOUR_ACCESS_TOKEN
You do not need to extract, store, or pass the user token manually after calling
HosteDay.auth.signInWithEmailAndPassword(...).
HTTP Requests
Use HosteDay.client for custom API requests.
final response = await
HosteDay.client.get
('/api/items
'
);
print(
response
);
All successful requests return decoded JSON as:
Map<String, dynamic>
GET
final response = await
HosteDay.client.get
('/api/items
'
,withAuth:
true
,
);
POST
final response = await
HosteDay.client.post
('/api/items
'
,withAuth: true,
body: <String, dynamic>{
'title': 'New item',
'description': 'Item description',
},
);
PUT
final response = await
HosteDay.client.put
('/api/items/1
'
,withAuth: true,
body: <String, dynamic>{
'title': 'Updated item',
},
);
PATCH
final response = await
HosteDay.client.patch
('/api/items/1
'
,withAuth: true,
body: <String, dynamic>{
'status': 'published',
},
);
DELETE
final response = await
HosteDay.client.delete
('/api/items/1
'
,withAuth:
true
,
);
Project API Token
Some HosteDay projects may require a project-level API token through the X-Api-Token header.
Configure it during initialization:
await
HosteDay.initializeApp
(
options: const <String, Object?>{
HosteDayOptionKeys.projectDomain: 'your-project.hosteday.com',
HosteDayOptionKeys.apiToken: 'PROJECT_API_TOKEN',
},
);
The SDK includes this header automatically in all HTTP requests:
X-Api-Token: PROJECT_API_TOKEN
A project API token and a user bearer token can be used together.
Realtime
HosteDay supports Pusher-compatible Realtime services through HosteDay.client.realtime.
Initialize Realtime settings once:
await
HosteDay.initializeApp
(
options: const <String, Object?>{
HosteDayOptionKeys.projectDomain: 'your-project.hosteday.com',
HosteDayOptionKeys.realtimeHost: 'ws3.hosteday.com',
HosteDayOptionKeys.pusherKey: 'YOUR_PUSHER_KEY',
},
);
Connect when Realtime is needed:
await
HosteDay.connectRealtime
();
You can inspect the generated endpoint:
print
(
HosteDay
.
config
.
realtimeUrl
);
Example:
wss://ws3.hosteday.com:443/app/YOUR_PUSHER_KEY
Public Channel
Public channels do not require user authentication.
await
HosteDay.connectRealtime
();
final subscription = await
HosteDay.client.realtime.listenPublic
(
channel: 'tenant.chat.room.1',
event: 'message.sent',
onEvent: (event) {
print(event.name);
print(event.channelName);
print(event.payload);
print(event.message);
},
);
Private Channel
Private channels require a signed-in user.
await
HosteDay.connectRealtime
();
final subscription = await
HosteDay.client.realtime.listenPrivate
(
channel: 'tenant.chat.room.1',
event: 'message.sent',
onEvent: (event) {
print(event.message);
print(event.userId);
print(event.userName);
print(event.userEmail);
},
);
The SDK automatically adds the private- prefix when it is missing.
The current HosteDay authentication token is automatically included while authorizing the channel.
Presence Channel
Presence channels require a signed-in user and can track connected members.
await
HosteDay.connectRealtime
();
await
HosteDay.client.realtime.listenPresence
(
channel: 'tenant.chat.room.1',
event: 'message.sent',
onEvent: (event) {
print(event.payload);
},
);
await HosteDay.client.realtime.listenPresenceMemberAdded(
channel: 'tenant.chat.room.1',
onEvent: (event) {
print('Member joined: ${event.payload}');
},
);
await HosteDay.client.realtime.listenPresenceMemberRemoved(
channel: 'tenant.chat.room.1',
onEvent: (event) {
print('Member left: ${event.payload}');
},
);
The SDK automatically adds the presence- prefix when it is missing.
Unified Realtime Listener
Use the unified listen(...) method when you need to select the channel type dynamically.
final subscription = await
HosteDay.client.realtime.listen
(
channel: 'tenant.chat.room.1',
event: 'message.sent',
type: HosteDayChannelType.private,
onEvent: (event) {
print(event.payload);
},
);
Supported channel types:
HosteDayChannelType.public
HosteDayChannelType.private
HosteDayChannelType.presence
HosteDayChannelType.privateEncrypted
Publish Realtime Events
Public Event
final response = await
HosteDay.client.publishPublicEvent
(
channel: 'tenant.chat.room.1',
event: 'message.sent',
payload: <String, dynamic>{
'message': 'Hello from Flutter',
},
);
print
(
response
);
Private Event
Private event publishing requires an authenticated user.
final response = await
HosteDay.client.publishPrivateEvent
(
channel: 'private-tenant.chat.room.1',
event: 'message.sent',
payload: <String, dynamic>{
'message': 'Private message',
},
);
print
(
response
);
Presence Event
final response = await
HosteDay.client.publishPresenceEvent
(
channel: 'tenant.chat.room.1',
event: 'member.typing',
payload: <String, dynamic>{
'typing': true,
},
);
print
(
response
);
publishPresenceEvent(...) automatically adds the presence- prefix when needed.
Unsubscribe and Disconnect
Unsubscribe from a channel without closing the WebSocket connection:
await
HosteDay.client.realtime.unsubscribe
('tenant.chat.room.1
'
,type:
HosteDayChannelType
.
private
,
);
Disconnect all Realtime channels and close the WebSocket connection:
await
HosteDay.client.realtime.disconnect
();
Release the full SDK when your application no longer needs it:
await
HosteDay.dispose
();
Configuration
HosteDay uses HosteDayConfig internally.
The SDK configuration is created through:
HosteDay.initializeApp
(
options: {
// HosteDay options.
},
);
Core Options
| Option key | Required | Description |
|---|---|---|
project_domain |
Yes | Your HosteDay project domain, such as example.hosteday.com. |
api_base_url |
No | Overrides the API base URL. |
base_url |
No | Alternative key for overriding the API base URL. |
X-Api-Token |
No | Project-level API token sent with all requests. |
pusher_key |
Realtime only | Pusher-compatible application key. |
realtime_host |
No | Realtime server host. Defaults to the API host. |
realtime_scheme |
No | Defaults to wss. |
realtime_port |
No | Defaults to 443. |
Path Override Options
| Option key | Default value |
|---|---|
login_path_post |
/api/auth/login |
register_path_post |
/api/auth/register |
forgot_password_path_post |
/api/auth/forgot-password |
reset_password_path_post |
/api/auth/reset-password |
user_show_path_get |
/api/user |
user_update_path_put |
/api/user |
user_update_avatar_path_post |
/api/user/avatar |
user_delete_path_delete |
/api/user |
logout_path_post |
/api/logout |
email_verify_path_post |
/api/email/verify |
public_events_path |
/api/realtime/events/public |
private_events_path |
/api/realtime/events/private |
presence_events_path |
/api/realtime/events/presence |
broadcasting_auth_path |
/api/broadcasting/auth-manual |
Example with custom paths:
await
HosteDay.initializeApp
(
options: const <String, Object?>{
HosteDayOptionKeys.projectDomain: 'your-project.hosteday.com',
'login_path_post': '/api/v1/auth/login',
'user_show_path_get': '/api/v1/me',
'broadcasting_auth_path': '/api/v1/broadcasting/auth',
},
);
Backend Authentication Response
The login and register endpoints must return a user and an access token.
Recommended Laravel response:
{
"access_token": "1|example-token",
"token_type": "Bearer",
"expires_in": null,
"user": {
"id": 1,
"name": "Mustafa",
"email": "mustafa@example.com",
"email_verified": true,
"avatar_url": null
}
}
The SDK also accepts common alternatives:
{
"token": "1|example-token",
"user": {
"id": 1,
"name": "Mustafa",
"email": "mustafa@example.com"
}
}
Or:
{
"data": {
"access_token": "1|example-token",
"user": {
"id": 1,
"name": "Mustafa",
"email": "mustafa@example.com"
}
}
}
Supported access-token keys:
access_token
accessToken
token
Supported user identifiers:
id
user_id
uuid
Error Handling
All network and API failures are reported through HosteDayException.
Authentication failures are reported through HosteDayAuthException.
try {
final credential = await HosteDay.auth.signInWithEmailAndPassword(
email: 'user@example.com',
password: 'password',
);
print(credential.user.email);
} on HosteDayAuthException catch (error) {
print(error.code);
print(error.message);
print(error.statusCode);
print(error.error);
} on HosteDayException catch (error) {
print(error.message);
print(error.statusCode);
print(error.error);
} catch (error) {
print(error);
}
Typical authentication error codes include:
invalid-email
wrong-password
invalid-credentials
email-already-in-use
validation-failed
permission-denied
no-current-user
malformed-auth-response
auth-request-failed
Migration from Hosteday
The old entry point is deprecated:
Hosteday.initializeApp
(...)Hosteday.
client
Hosteday
.
config
Use the new official API:
HosteDay.initializeApp
(...)HosteDay.
client
HosteDay
.
config
HosteDay
.
auth
Before
final tokenProvider = StaticHosteDayTokenProvider(
'USER_TOKEN_HERE',
);
await
Hosteday.initializeApp
(
options: {
'project_domain': 'example.hosteday.com',
},
tokenProvider: tokenProvider,
);
final response = await Hosteday.client.post(
Hosteday.config.loginPathPost,
body: {
'email': email,
'password': password,
},
);
After
await
HosteDay.initializeApp
(
options: {
'project_domain': 'example.hosteday.com',
},
);
final credential = await HosteDay.auth.signInWithEmailAndPassword(
email: email,
password: password,
);
final response = await HosteDay.client.get(
'/api/user',
withAuth: true,
);
print(credential.user.email);
print
(
response
);
The access token is now stored and retrieved internally by HosteDay.
Example Application
A complete Flutter example is included in this repository:
The example demonstrates:
- Authentication gate.
- Sign in and registration.
- Session-aware API requests.
- Current user reload and update.
- Password reset requests.
- Email verification requests.
- Public, private, and presence Realtime channels.
- Publishing and receiving Realtime events.
- Automatic session cleanup on sign out.
Security Notes
- Never hard-code production user tokens in source code.
- Use a secure
HosteDayAuthStorageimplementation in production. - Keep project API tokens out of public repositories.
- Validate authorization and tenant ownership in your Laravel backend.
- Do not trust client-side checks as a replacement for server-side authorization.
- Use protected Realtime channel authorization for sensitive events.
- Revoke or invalidate tokens server-side when your security model requires it.
License
This project is licensed under the MIT License.