vps_utilities 1.3.1 copy "vps_utilities: ^1.3.1" to clipboard
vps_utilities: ^1.3.1 copied to clipboard

A multiplatform utilities package for VPS Citizen Mobile application. Supports iOS, Android, Web, Windows, macOS, and Linux. Provides network layer, secure storage, token management, registry, form en [...]

vps_utilities #

A multiplatform utilities package for VPS Citizen Mobile application. It runs on all Flutter-supported platforms from a single codebase.

Supported platforms: iOS, Android, Web, Windows, macOS, Linux.

Author: Levis Nyingi

License: Private / proprietary. This software is for use only in ISE Intelligent Security Ecosystem software. See LICENSE.

Provides a network layer (HTTP client with Dio), base URL config, secure token storage, flexible secure key-value storage (drop-in replacement for an app-level secure storage data source), Cubit state management for tokens, registry (shared models and data source for officer/citizen registry, stations, and officers), form engine (configurable forms with paging, validation, optional AI narrative, and context.pushForm), cache, device info, and responsive layout plus device type detection for platform-aware UI, using Clean Architecture and Kiwi for dependency injection. Storage uses flutter_secure_storage (encrypted where supported, persists across restarts). No Material UI—works on mobile, desktop, and web.

Full documentation #

Complete documentation for every area of the package is in the docs folder:

Topic Doc
Initialization, core, DI docs/initialization.md
Network layer, API errors, guardedApiCall docs/network.md
Token API, device token, TokenCubit docs/token.md
Secure storage (flexible key-value) docs/secure_storage.md
Cache (CacheManager) docs/cache.md
Device info (DeviceInfoProvider) docs/device_info.md
Responsive layout, device type docs/responsive.md
Registry (shared models, RegistryDataSource) docs/registry.md
Form engine (VpsFormEngine, pushForm) docs/form_engine.md
Narrative (AI narrative service) docs/narrative.md

docs/README.md – Index of all documentation.

Installation #

From a local path (same repo) #

Add this package to your pubspec.yaml:

dependencies:
  vps_utilities:
    path: flutter packages/vps_utilities

From GitHub #

If the package is stored in GitHub, depend on it via git:

dependencies:
  vps_utilities:
    git:
      url: https://github.com/YOUR_ORG_OR_USER/vps_utilities.git
      ref: main   # or master, or a tag e.g. ref: v1.1.0

For a private repo, use SSH or a personal access token in the URL. Then run:

flutter pub get

Platform notes #

The package is written in Dart/Flutter and does not depend on platform-specific APIs directly. Secure token storage uses flutter_secure_storage, which has platform-specific implementations (e.g. Keychain on iOS, Keystore on Android, in-memory or alternatives on Web). The same API works on all platforms; behavior and backing store may differ per platform.

Initialization #

Important: You must initialize the package with a base URL before using any functionality.

Initialize the package in your main() function before running your app:

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

void main() {
  VpsUtilities.initialize(
    baseUrl: 'https://api.example.com/api/v1/',
  );

  runApp(MyApp());
}

Network layer #

Resolve and use the HTTP client via Kiwi:

final networkService = VpsUtilities.resolve<NetworkService>();

// GET
final response = await networkService.getHttp('/users', tokenRequired: true);

// POST
final response = await networkService.postHttp(
  '/login',
  body: {'email': email, 'password': password},
  tokenRequired: false,
);

Methods: getHttp, postHttp, putHttp, deleteHttp. Each accepts endpoint, optional params, headers, body (where applicable), tokenRequired, and optional per-request token.

Response shape: On success the map has data. On failure it has error (an [ApiErrors] value) and message. Use response['error'] as ApiErrors for handling (e.g. 401 → unauthenticated, 404 → notFound).

Token API #

Set #

Store the auth token securely (e.g. after login). Pass null to clear (e.g. on logout). Stored with flutter_secure_storage (encrypted, persists across restarts).

await VpsUtilities.setAuthToken(response['data']['token']);

// Clear on logout
await VpsUtilities.setAuthToken(null);

Device token (Device-Token header) #

In addition to the auth token, the package supports a device registration token that is sent as a Device-Token HTTP header on all requests when present. This is intended for device onboarding flows shared across apps (e.g. Officer, Citizen, others).

Set / clear device token

// After successful device registration
await VpsUtilities.setDeviceToken(deviceTokenFromBackend);

// Clear when needed (e.g. logout, reset)
await VpsUtilities.setDeviceToken(null);

Read device token (optional)

final deviceToken = await VpsUtilities.getDeviceToken();

How it is used in requests

The internal RestClient reads the stored device token from secure storage and, if non-empty, adds:

  • Device-Token: <device_registration_token>

to every outgoing request. If no token is stored, the header is omitted and behavior is unchanged.

Use #

When tokenRequired: true, the package adds the Authorization: Bearer <token> header automatically. It uses a per-request token if you pass one; otherwise it uses the stored token.

Per-request token #

Each network method accepts an optional token parameter. If provided, that token is used for that request only (even if no token was set globally). Use this for one-off or login requests.

final networkService = VpsUtilities.resolve<NetworkService>();

// One-off request with a specific token
await networkService.getHttp(
  '/profile',
  tokenRequired: true,
  token: someToken,
);

See #

Read the stored auth token when needed. Sensitive: use only when necessary (e.g. debug or passing to another layer). Do not log or display in production.

final token = await VpsUtilities.getAuthToken();

Secure storage (flexible key-value) #

The package provides flexible secure storage for any key-value data (tokens, user data, etc.). It uses the same encrypted backend as the auth token (Android EncryptedSharedPreferences, iOS Keychain, etc.) and can replace an app-level secure storage data source.

Requirements: Call VpsUtilities.initialize(baseUrl: ...) first.

API (static) #

Method Description
VpsUtilities.setSecureData(key, value) Store value under key. Pass null or empty to remove the key.
VpsUtilities.getSecureData(key) Returns Future<String?> — the value for key, or null if not found.
VpsUtilities.removeSecureData(key) Removes the value for key.
VpsUtilities.clearAllSecureData() Removes all keys. Use with care (e.g. on logout).
VpsUtilities.containsSecureKey(key) Returns Future<bool> — whether key exists.

Examples #

// Store sensitive data
await VpsUtilities.setSecureData('user', jsonEncode(userJson));

// Read
final userJson = await VpsUtilities.getSecureData('user');

// Check existence
final hasUser = await VpsUtilities.containsSecureKey('user');

// Remove one key
await VpsUtilities.removeSecureData('user');

// Clear all (e.g. logout)
await VpsUtilities.clearAllSecureData();

Resolving SecureStorage #

For dependency injection or multiple operations, resolve the [SecureStorage] instance:

final storage = VpsUtilities.resolve<SecureStorage>();

await storage.write('key', 'value');
final value = await storage.read('key');
await storage.delete('key');
await storage.deleteAll();
final exists = await storage.containsKey('key');

Replacing an app-level secure storage #

If your app has a SecureStorageDataSource (or similar) that uses flutter_secure_storage with storeSecureData / getSecureData / removeSecureData / clearAllSecureData / containsKey, you can route those calls to VpsUtilities.setSecureData, VpsUtilities.getSecureData, etc., after initializing the package. The auth token is already handled via Token API; use the flexible API for other sensitive keys (e.g. user profile, IPRS data).

Token usage examples #

After login:

await VpsUtilities.setAuthToken(response['data']['token']);

One-off request with a different token:

final networkService = VpsUtilities.resolve<NetworkService>();
await networkService.getHttp(
  url,
  tokenRequired: true,
  token: oneOffToken,
);

Accessing the base URL:

String baseUrl = VpsUtilities.baseUrl;

// Or via domain contract
BaseUrlConfigRepository config = VpsUtilities.resolve<BaseUrlConfigRepository>();
String baseUrl = config.baseUrl;

State management (Cubit) #

The package provides [TokenCubit] for token state. It syncs with the secure token store and emits [TokenState] (e.g. hasToken, optional token). Use [BlocProvider] and [BlocBuilder] / [BlocListener] in your app.

Provide the cubit:

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:vps_utilities/vps_utilities.dart';

// After VpsUtilities.initialize(...)
BlocProvider<TokenCubit>(
  create: (_) => VpsUtilities.resolve<TokenCubit>(),
  child: MyApp(),
)

React to token state:

BlocBuilder<TokenCubit, TokenState>(
  builder: (context, state) {
    if (state.hasToken) {
      return HomeScreen();
    }
    return LoginScreen();
  },
)

Update token (login / logout):

// After login
context.read<TokenCubit>().setToken(response['data']['token']);

// Logout
await context.read<TokenCubit>().clearToken();

Calling [VpsUtilities.setAuthToken] also updates [TokenCubit] state so the UI stays in sync.

Responsive layout and device type #

The package provides Tailwind-style breakpoints, device/platform detection, and layout helpers so you can build responsive, platform-aware UI in one place.

Device type (no BuildContext) #

Use [VpsResponsive] for quick checks. It uses the registered [DeviceTypeProvider] when [VpsUtilities] is initialized; otherwise it falls back to [DeviceTypeProviderImpl], so device type works with or without DI.

import 'package:vps_utilities/vps_utilities.dart';

// Quick checks (no initialization required for fallback)
if (VpsResponsive.isWeb) { /* web-only UI */ }
if (VpsResponsive.isAndroid) { /* Android-specific */ }
if (VpsResponsive.isIOS) { /* iOS-specific */ }
if (VpsResponsive.isMobileOS) { /* Android or iOS */ }

// Full enum (e.g. for switch or logging)
DeviceType type = VpsResponsive.deviceType;

Resolve via DI (after [VpsUtilities.initialize]):

final provider = VpsUtilities.resolve<DeviceTypeProvider>();
DeviceType type = provider.current;

Responsive layout (with BuildContext) #

Use [ResponsiveLayout] with [MediaQuery] and Tailwind breakpoints (sm 640, md 768, lg 1024, xl 1280, 2xl 1536). All methods take [BuildContext].

Breakpoints (mobile-first, “at least”):

if (ResponsiveLayout.isSm(context)) { /* width >= 640 */ }
if (ResponsiveLayout.isMd(context)) { /* width >= 768 */ }
if (ResponsiveLayout.isLg(context)) { /* width >= 1024 */ }
if (ResponsiveLayout.isXl(context)) { /* width >= 1280 */ }
if (ResponsiveLayout.is2xl(context)) { /* width >= 1536 */ }

Orientation:

if (ResponsiveLayout.isPortrait(context)) { /* portrait */ }
if (ResponsiveLayout.isLandscape(context)) { /* landscape */ }

Layout helpers:

// Responsive padding (16 below md, 24 at md–lg, 32 at lg+)
padding: ResponsiveLayout.getResponsivePadding(context),

// Max content width for centered layouts (infinity / 900 / 1200 by breakpoint)
maxWidth: ResponsiveLayout.getMaxContentWidth(context),

// Center content with max width when at least md
ResponsiveLayout.centerContent(
  context: context,
  child: myWidget,
  maxWidth: 1200, // optional; defaults to getMaxContentWidth(context)
)

Breakpoint constants (for custom logic): [TailwindBreakpoints.sm], [TailwindBreakpoints.md], [TailwindBreakpoints.lg], [TailwindBreakpoints.xl], [TailwindBreakpoints.xxl].

Pure width-based checks (no [BuildContext]): use [BreakpointResolver] with a width value (e.g. from [MediaQuery.sizeOf].width):

double width = MediaQuery.sizeOf(context).width;
if (BreakpointResolver.isMd(width)) { /* width >= 768 */ }

When to use what #

Need Use
“Am I on web / Android / iOS?” [VpsResponsive.isWeb], [VpsResponsive.isAndroid], [VpsResponsive.isIOS], or [VpsResponsive.deviceType]
“Is viewport at least sm/md/lg?” [ResponsiveLayout.isSm]/[isMd]/isLg
“Portrait or landscape?” [ResponsiveLayout.isPortrait]/isLandscape
“Padding / max width by breakpoint?” [ResponsiveLayout.getResponsivePadding], [ResponsiveLayout.getMaxContentWidth]
“Center content with max width?” [ResponsiveLayout.centerContent]
“Width >= breakpoint without context?” [BreakpointResolver.isSm]/[isMd]/…(width)

Registry #

The package provides a shared registry utility for VPS Officer and VPS Citizen apps: shared models (registry entries, stations, officers) and a [RegistryDataSource] that uses the package [NetworkService] and [guardedApiCall] to fetch registry data. No app-specific network layer is required; the registry lives in the package and reuses the package network. The form engine uses [SubModule] and [Field] from the registry. See Registry for full documentation.

Purpose #

  • Officer app: Fetch registry list (microservices/modules) via a single endpoint (e.g. officer_registry), typically without auth.
  • Citizen app: Fetch registry plus police stations and officers in one response (endpoint e.g. registry), with auth; optional fields such as initial_iprs and documents are supported in the shared models.

Models use optional fields so one canonical type works for both apps: e.g. [RegistryModel] has optional initial_iprs and documents (citizen uses them; officer receives null when the API omits them). [RegistryDatum] has optional initial_iprs. [StationResponseModel] and [StationOfficer] are used when the citizen app’s registry response includes stations and officers.

Resolving and using RegistryDataSource #

After Initialization, resolve the data source and call the appropriate method:

final registryDataSource = VpsUtilities.resolve<RegistryDataSource>();

// Officer-style: registry list only, no token
final registryList = await registryDataSource.getRegistry(tokenRequired: false);

// Citizen-style: registry + stations + officers in one call (with token)
final result = await registryDataSource.getRegistryWithStationsAndOfficers(
  tokenRequired: true,
);
// result.data   -> List<RegistryModel>
// result.stations -> List<StationResponseModel>? (may be null if API omits)
// result.officers -> List<StationOfficer>? (may be null if API omits)

On API or network failure, both methods throw [GuardedApiException]; apps can catch it and map to their own domain errors or UI messages.

Endpoint and token #

The default registration in the package uses endpoint registry and defaultTokenRequired: false. For the officer app (endpoint officer_registry, no token), register your own [RegistryDataSourceImpl] in app DI after setup, e.g.:

// In app DI, after VpsUtilitiesInjector.setup(baseUrl):
container.registerFactory<RegistryDataSource>(
  (c) => RegistryDataSourceImpl(
    c.resolve<NetworkService>(),
    registryEndpoint: 'officer_registry',
    defaultTokenRequired: false,
  ),
);

Alternatively, the package may be extended later to accept optional registryEndpoint and registryTokenRequired from VpsUtilities.initialize.

Exported types #

  • Contracts: [RegistryDataSource]
  • Result: [RegistryResult] (data, stations, officers)
  • Implementation: [RegistryDataSourceImpl]
  • Models: [RegistryModel], [RegistryDatum], [SubModule], [Field], [Conditions], [Dependency], [InputField], [StationResponseModel], [StationOfficer]

Full documentation: Registry.

Form engine #

The package provides a configurable form engine with paging, validation, optional AI narrative enhancement (Q&A and review dialogs), and a single-call API so the app can open a form with one line.

Quick start #

  1. Initialize with base URL and optional AI narrative URL (see Initialization).
  2. Register the form engine config after initialization:
VpsUtilities.formEngineConfig = VpsFormEngineConfig(
  pageConfig: MyFormPageConfig(),   // implements VpsFormPageConfig
  theme: VpsFormTheme(...),         // optional
  fieldBuilderRegistry: myRegistry, // optional; custom field builders
);
  1. Open a form from anywhere with a BuildContext. For per-module base URLs (e.g. from registry), set your page config’s module URL before calling pushForm (e.g. pageConfig.moduleBaseUrl = registryModel.url). See Form engine.
context.pushForm(subModule);  // subModule is the package's SubModule (from registry)

When the user taps Submit on the last page, the engine validates, runs the AI narrative flow (if enabled), then optionally runs a pre-confirm flow (e.g. occurrence location and police station selection) when your page config returns true for shouldRunPreConfirmFlow, then pushes the default confirm page (or your custom [buildConfirmPage]). The default confirm page matches vps_citizen: step indicator (dynamic steps; optional show/hide and numbered/unnumbered), details card with module name and field rows, Edit and Submit buttons. When the user taps Submit on the confirm page, the engine submits to sub_module_data and pushes the default success or fail page. See Form engine for pre-confirm flow (OB flow) details.

Main types #

  • [VpsFormPageConfig] – App implements this for baseUrl, optional mapsApiKey (for location fields), and optional custom narrative/success/fail dialogs. For per-module base URLs (e.g. from registry), add a settable moduleBaseUrl and have baseUrl return moduleBaseUrl ?? VpsUtilities.baseUrl; set moduleBaseUrl before each pushForm.
  • [VpsFormEngineConfig] – Bundles pageConfig, optional theme, optional fieldBuilderRegistry, optional getAuthToken, and optional step indicator overrides: initialStep, showStepIndicator, showStepNumbers. See Form engine.
  • [FieldBuilderRegistry] – Register custom builders by field type (e.g. location, mugshot) or by field name.
  • [VpsFormEngine] – Root form widget (Scaffold, AppBar, step indicator, form content).
  • context.pushForm(SubModule) – Extension to open the form using the registered config.

Full documentation: Form engine.

Architecture #

The package follows Clean Architecture and is organized by utility so multiple utilities are easy to add and maintain:

lib/src/
  core/                    # Shared: config + DI (used by other utilities)
    domain/                # BaseUrlConfigRepository
    data/                  # BaseUrlConfigRepositoryImpl
    di/                    # VpsUtilitiesInjector (Kiwi container)
  network/                 # Network utility
    domain/                # NetworkService, ApiErrors, guardedApiCall
    data/                  # NetworkServiceImpl, RestClient, ErrorSanitizer
  registry/                # Registry utility (uses network)
    domain/                # RegistryDataSource, RegistryResult, models
    data/                  # RegistryDataSourceImpl
  form/                    # Form engine (uses core, narrative)
    domain/                # Ports, NarrativeKeysHelper
    data/                  # FormDependencyResolverImpl, FormValidationServiceImpl
    presentation/          # VpsFormCubit, VpsFormEngine, config, field builders, pushForm
  narrative/               # AI narrative enhancement (uses core)
    domain/                # NarrativeServicePort
    data/                  # NarrativeServiceImpl, NoOpNarrativeService
  secure_storage/          # Flexible secure key-value storage
    secure_storage_impl.dart  # SecureStorage (flutter_secure_storage)
  token/                   # Token utility (uses SecureStorage)
    data/                  # TokenStore
    presentation/cubit/    # TokenCubit, TokenState
  responsive/              # Responsive layout and device type
    domain/                # DeviceType, TailwindBreakpoints, DeviceTypeProvider
    data/                  # DeviceTypeProviderImpl, BreakpointResolver
    presentation/          # ResponsiveLayout, VpsResponsive
  vps_utilities_init.dart  # Package init and public API
  • core: Base URL config and Kiwi DI; wires all utilities. Initialize with VpsUtilities.initialize(baseUrl: ...).
  • network: HTTP client (domain contracts + data implementations; uses core for base URL and token utility for Bearer token).
  • registry: Shared registry, station, and officer models plus [RegistryDataSource] for fetching registry (and optionally stations/officers) via the package [NetworkService] and [guardedApiCall]. See Registry.
  • form: Configurable form engine with paging, validation, optional AI narrative flow, [FieldBuilderRegistry], and [context.pushForm]. See Form engine and Form engine.
  • narrative: AI narrative enhancement service ([NarrativeServicePort]); real impl when aiNarrativeBaseUrl is set, no-op otherwise. Used by the form engine.
  • secure_storage: Flexible secure key-value storage ([SecureStorage]); same backend as token (Android EncryptedSharedPreferences, iOS Keychain, etc.). Use Secure storage (flexible key-value) for API.
  • token: Secure token storage and TokenCubit/TokenState for UI; uses [SecureStorage] with a fixed key.
  • responsive: Device type ([DeviceType], [DeviceTypeProvider]) and Tailwind-style layout ([TailwindBreakpoints], [ResponsiveLayout], [BreakpointResolver]). Use [VpsResponsive] for device checks and Responsive layout and device type for API.

To add a new utility (e.g. analytics, logging): add a folder under src/ with domain/, data/, and optional presentation/, then register it in core/di/vps_utilities_injector.dart.

Dependencies are resolved via Kiwi. After initialization, use VpsUtilities.resolve<T>() to obtain registered types (e.g. NetworkService, BaseUrlConfigRepository, TokenCubit).

Regenerating Kiwi code #

If you add or change @Register in the injector, regenerate the .g.dart file:

cd packages/vps_utilities
dart run build_runner build --delete-conflicting-outputs
0
likes
110
points
13
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

A multiplatform utilities package for VPS Citizen Mobile application. Supports iOS, Android, Web, Windows, macOS, and Linux. Provides network layer, secure storage, token management, registry, form engine, cache, device info, responsive layout, and AI narrative enhancement.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

bloc, cached_network_image, dartz, device_info_plus, dio, equatable, flutter, flutter_bloc, flutter_form_builder, flutter_secure_storage, form_builder_validators, freezed_annotation, google_maps_flutter, http, image_picker, intl, intl_phone_number_input, json_annotation, kiwi, logger, meta, path, path_provider, place_picker_google, shared_preferences, table_calendar

More

Packages that depend on vps_utilities