hosteday_flutter 2.3.0 copy "hosteday_flutter: ^2.3.0" to clipboard
hosteday_flutter: ^2.3.0 copied to clipboard

A lightweight Flutter SDK for connecting apps with Hosteday APIs, including authentication, user requests, custom endpoints, and realtime support.

hosteday_flutter #

Flutter SDK for Hosteday REST, authentication, public URLs, and optional Realtime.

Installation #

Add the package to your Flutter application:

dependencies:
  hosteday_flutter: ^2.3.0
import 'package:hosteday_flutter/hosteday_flutter.dart';

Initialize Hosteday #

Only the project domain is required. No network request is made by the default initialization.

await Hosteday.initializeApp(options: {
  HostedayOptionKeys.projectDomain: 'max.hosteday.com',
});

Optional values may be null. http://localhost:8787 preserves HTTP and the port. Call await Hosteday.dispose() before switching projects.

Generated app runtime config #

Declare the asset in the application's pubspec.yaml:

flutter:
  assets:
    - assets/config/hosteday.json
{
  "project_domain": "max.hosteday.com",
  "project_api_key": null,
  "realtime_app_key": null,
  "realtime_host": null
}
WidgetsFlutterBinding.ensureInitialized();
await Hosteday.initializeFromAsset();

initializeFromAsset(overrides: {...}) supports explicit option overrides. The engine only needs to supply the asset and ordinary SDK resource calls.

REST API #

Relative resource paths automatically receive /api. products, /products, and /api/products all target https://max.hosteday.com/api/products.

Read a page or a record #

final page = await Hosteday.client.index('products',
  search: 'phone', page: 1, withAuth: false,
);
final products = page.items; // List<Map<String, dynamic>>
print(page.currentPage);
print(page.perPage);
print(page.hasNextPage);
print(page.hasPreviousPage);

final product = await Hosteday.client.show('products', id: 1, withAuth: false);
print(product['name']);

index unwraps data.data into HostedayPage.items; show unwraps data into a Map<String, dynamic>. Neither requires a resource-specific key. Both default to withAuth: false and accept headers, timeout, query parameters, and relation scoping. index also supports filters.

Raw GET (unchanged) #

final response = await Hosteday.client.get('products');
final recordResponse = await Hosteday.client.get('products', id: 1);

get returns the complete response envelope, including success, message, and data, for applications that need raw responses.

POST #

final created = await Hosteday.client.post('/products',
  body: {'name': 'Phone', 'price': 250},
  withAuth: true,
);

PUT #

await Hosteday.client.put('/products', id: 15,
  body: {'name': 'Phone', 'price': 240},
);

PATCH #

await Hosteday.client.patch('/products/15', body: {'price': 230});

DELETE #

await Hosteday.client.delete('/products', id: 15);

id is optional for PUT/PATCH/DELETE when it is already part of the path. When supplied, it is appended as one encoded segment. The high-level client's mutation methods retain withAuth: true as their default; GET and request default to false. The low-level http methods retain their default of false.

Host and URLs #

final host = Hosteday.client.host;             // https://max.hosteday.com
final api = Hosteday.client.apiBaseUrl;        // https://max.hosteday.com/api

Advanced API routing:

await Hosteday.initializeApp(options: {
  HostedayOptionKeys.projectDomain: 'max.hosteday.com',
  HostedayOptionKeys.apiBaseUrl: 'https://gateway.example.com/v2',
});
// /products -> https://gateway.example.com/v2/products
// host and public links still use https://max.hosteday.com

An override containing only an origin gets /api; an override with a path uses that exact prefix. base_url remains an alias. Full REST URLs must use the configured API origin, protecting project and user credentials from accidental forwarding. Full media URLs may use other origins.

Static Pages #

final info = Hosteday.client.staticPages['info'];
final privacy = Hosteday.client.staticPages['/privacy/'];
// https://max.hosteday.com/info
// https://max.hosteday.com/privacy

Dart reserves static, so the property is named staticPages. It accepts one non-empty slug and rejects traversal, query strings, fragments, and foreign URLs. It builds a link without fetching the page or checking the platform's page quota.

Storage & Media #

final imageUrl = Hosteday.client.storageUrl('products/image.png');
// https://max.hosteday.com/storage/products/image.png
final cdn = Hosteday.client.storageUrl('https://cdn.example.com/image.png');
// unchanged

An existing /storage/ prefix is not duplicated. Handle absent API image values before calling storageUrl:

final imagePath = product['image'] as String?;
if (imagePath != null && imagePath.isNotEmpty) {
  Image.network(Hosteday.client.storageUrl(imagePath));
}

Authentication #

final credential = await Hosteday.auth.signInWithEmailAndPassword(
  email: 'customer@example.com', password: password,
);
final orders = await Hosteday.client.get('/orders', withAuth: true);
await Hosteday.auth.signOut();
await Hosteday.auth.createUserWithEmailAndPassword(
  email: email, password: password,
  additionalData: {'name': 'Customer'},
);
await Hosteday.auth.sendPasswordResetEmail(email: email);
await Hosteday.auth.reload();
await Hosteday.auth.sendEmailVerification();

Observe Hosteday.auth.authStateChanges() and read Hosteday.auth.currentUser. For profile, avatar, password reset, session, and channel details see the API reference.

Default auth storage is in memory. To persist sessions:

await Hosteday.initializeApp(
  options: {HostedayOptionKeys.projectDomain: 'max.hosteday.com'},
  authStorage: HostedaySharedPreferencesAuthStorage(),
);

SharedPreferences is not encrypted storage. You can implement HostedayAuthStorage for your platform's secure storage. For an external user session, supply a HostedayTokenProvider; the SDK's active session takes priority.

Project API Key #

await Hosteday.initializeApp(options: {
  HostedayOptionKeys.projectDomain: 'max.hosteday.com',
  HostedayOptionKeys.projectApiKey: projectApiKey,
});

The existing platform header is preserved: X-Api-Token. Null, empty, and whitespace-only keys send no header. apiTokenHeader supports an explicit server-specific override. This key is separate from Authorization: Bearer ..., which is added only for withAuth: true (or an explicitly supplied header). Client configuration is visible in a compiled application; use client-scoped keys.

final products = await Hosteday.client.get('/products',
  search: 'phone',
  queryParameters: {'category_id': 5, 'page': 2},
  filters: {'status': ['active', 'pending']},
);

Existing URL query values are preserved unless overridden. Null values are omitted. Nested maps and lists use Laravel bracket syntax. Filters apply to index GET requests only. Relation scoping remains available:

await Hosteday.client.get('/products', id: 15,
  relationField: 'category_id', relationValue: 5,
);

Pagination / Responses #

final page = await Hosteday.client.index('products', page: 1, search: 'phone');
if (page.hasNextPage) {
  final next = await Hosteday.client.index('products',
    page: page.currentPage + 1, search: 'phone',
  );
  print(next.items);
}
if (page.hasPreviousPage) {
  final previous = await Hosteday.client.index('products',
    page: page.currentPage - 1, search: 'phone',
  );
  print(previous.items);
}

Keep the same search, filters, and relation options when requesting another page. page must be positive and overrides queryParameters['page'] when supplied. nextPageUrl and previousPageUrl are server metadata only. Requests use the configured API domain, never the HTTP links returned in pagination metadata. from and to may be null; total and lastPage are nullable and are never inferred from record counts. Empty lists are valid. No automatic local sorting, filtering, field coercion, or fetching of additional pages occurs.

index and show require success: true and the documented envelope shape. Malformed responses and success: false throw HostedayException, retaining server messages, validation details, and the original response. HTTP errors continue to use the existing request-layer error handling.

get retains the server JSON structure without these envelope checks. Its existing behavior still wraps a top-level list as {'data': list} and returns {} for an empty body. The API explorer uses raw get to display full responses; the posts repository uses index and show.

Error Handling #

try {
  await Hosteday.client.index('products',
    search: 'phone', page: 1, timeout: const Duration(seconds: 15));
} on HostedayException catch (error) {
  final status = error.statusCode;
  final developerMessage = error.message;
  final userMessage = error.displayMessage;
  final originalCause = error.error;
  final emailError = error.firstErrorFor('email');
}

Network failures and timeouts have distinct messages. Invalid JSON retains the HTTP status and original body in response['raw_body']. HTTP errors retain the server message, status, and validation errors, including 429, 430–433, and 5xx. No undocumented meaning is assigned to Hosteday-specific status codes.

For compatibility, missing/invalid configuration throws ArgumentError, and access before initialization or repeated initialization throws StateError, with specific corrective messages. Runtime asset read/JSON errors use HostedayException.

Realtime #

REST works without Realtime configuration. To connect, supply both settings:

await Hosteday.initializeApp(options: {
  HostedayOptionKeys.projectDomain: 'max.hosteday.com',
  HostedayOptionKeys.realtimeAppKey: realtimeAppKey,
  HostedayOptionKeys.realtimeHost: realtimeHost,
});
if (Hosteday.config.hasRealtime) {
  await Hosteday.connectRealtime();
}

Listen using listenPublic, listenPrivate, or listenPresence as shown in the Realtime example. Private and presence subscriptions require user authentication. Publishing follows the same distinction:

await Hosteday.client.publishPublicEvent(
  channel: 'products', event: 'updated', payload: {'id': 15},
);
await Hosteday.client.publishPrivateEvent(
  channel: 'orders', event: 'updated', payload: {'id': 42},
);
await Hosteday.disconnectRealtime();

Complete Example #

This minimal app displays the real server response:

import 'package:flutter/material.dart';
import 'package:hosteday_flutter/hosteday_flutter.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Hosteday.initializeApp(options: {
    HostedayOptionKeys.projectDomain: 'max.hosteday.com',
  });
  final request = Hosteday.client.get('/products');
  runApp(MaterialApp(home: Scaffold(body: SafeArea(
    child: FutureBuilder<Map<String, dynamic>>(
      future: request,
      builder: (context, snapshot) {
        if (snapshot.hasError) return Text(snapshot.error.toString());
        if (!snapshot.hasData) return const CircularProgressIndicator();
        return SingleChildScrollView(child: Text(snapshot.data.toString()));
      },
    ),
  ))));
}

The interactive example includes all HTTP verbs, search, query parameters, real response rows and images, URL helpers, account flows, and optional Realtime. On Web, /categories is sent to /api/categories; CORS must be configured by the server.

Migration and verification #

Old HosteDay... spellings remain deprecated compatibility APIs. New code uses Hosteday.... See migration notes for URL behavior changes, architecture, and verification status.

flutter pub get
dart format lib test example/lib example/test
flutter analyze
flutter test
cd example
flutter pub get
flutter analyze
flutter test

This is a Flutter package: run its flutter_test suite with flutter test, not standalone dart test.

4
likes
160
points
136
downloads

Documentation

Documentation
API reference

Publisher

verified publisherhosteday.com

Weekly Downloads

A lightweight Flutter SDK for connecting apps with Hosteday APIs, including authentication, user requests, custom endpoints, and realtime support.

Homepage
Repository (GitHub)
View/report issues

Topics

#hosteday #flutter #api #sdk #realtime

License

MIT (license)

Dependencies

dart_pusher_channels, flutter, http, meta, shared_preferences

More

Packages that depend on hosteday_flutter