draftmode_framework 1.1.3
draftmode_framework: ^1.1.3 copied to clipboard
Reusable Flutter package for iOS-first native-feeling app foundations.
DraftModeFramework #
Reusable Flutter building blocks for shipping apps with a native look and feel without rebuilding the same page, section, row, dialog, navigation, and app-foundation patterns for every project.
The framework supports both iOS and Android through the same adaptive architecture for navigation, surfaces, actions, feedback, forms, and app-level building blocks. The primary goal of this repository today is to make it simple to assemble production-ready, native-feeling apps from reusable primitives and a growing app-foundation toolkit.
Product Goal #
This repository is meant to serve two connected roles today, while preparing for a broader app-foundation role over time:
- A reusable library in
lib/that currently focuses on UI, page, and navigation primitives and now also includes the first reusable forms, storage, and repository foundations. - A runnable
example/host app that consumes that library and shows how to compose those primitives into real screens.
The guiding architectural principles are:
- Keep layout, navigation scaffolding, and styling separate from business logic.
- Prefer reusable primitives over screen-specific one-offs.
- Keep the public API generic enough to be reused across apps.
- Use the example app as a reference host and template, not as the source of product logic.
Source Structure #
The public API stays available through stable root entrypoints in lib/, while
the implementation is organized into ownership-focused domains:
lib/core/for shared, non-visual foundationslib/core/validation*.dartfor shared validation contracts, contexts, and issue types used by forms, entities, and repositorieslib/ui/for rendering-focused widgets and theme primitiveslib/page/for page-level scaffoldslib/navigation/for navigation items and barslib/l10n/for framework-owned localization resources and maintained localization implementationlib/forms/for form state, validation, formatters, and reusable form componentslib/entities/for reusable field definitions and shared non-visual entity metadatalib/storage/for persistence contracts, adapters, and serializationlib/repositories/for typed entity-manager orchestration on top of storage
This keeps current imports stable while giving future modules clear landing zones.
Platform Status #
- iOS: fully supported.
- Android: fully supported for the framework-owned adaptive primitives, with ongoing polish as more surfaces migrate behind the same stable public APIs.
The example app currently uses CupertinoApp directly because that keeps the
library integration transparent and predictable while the framework resolves
platform-specific rendering internally. Consumers can still host the framework
from Cupertino or Material app shells depending on product needs.
The framework owns visual platform selection internally. Widgets resolve one
effective DraftModePlatformStyle from host context, and tests or future
framework wiring can override that style with DraftModePlatformScope or
DraftModeUIPlatform.runWithStyleOverride(...) without adding a required
platform flag to consuming apps. High-visibility grouped surfaces such as
sections, cards, dialogs, buttons, switches, text-field form inputs, and
navigation chrome follow this adaptive-facade pattern so consuming apps keep
stable imports while platform renderers evolve behind the scenes.
Date/time primitives follow the same model: iOS keeps the current inline Cupertino calendar and wheel-picker behavior, while Android uses framework- owned Material date/time dialogs with shared value normalization and range shifting logic under the same public APIs.
Library entrypoints #
| Import | Contents | Docs |
|---|---|---|
package:draftmode_framework/components.dart |
All UI components (dialog, list, card, button, …) | COMPONENTS.md |
package:draftmode_framework/pages.dart |
Page scaffolds and navigation bars | PAGES.md |
package:draftmode_framework/theme.dart |
DraftModeUIThemeData + DraftModeUITheme, including iosLight/iosDark/androidLight/androidDark factories |
THEME.md |
package:draftmode_framework/formatter.dart |
Duration formatter | FORMATTER.md |
package:draftmode_framework/context.dart |
DraftModeUIContext.init for shared navigator/context wiring |
— |
package:draftmode_framework/buttons.dart |
Platform-aware icon tokens via DraftModeUIButtons |
— |
package:draftmode_framework/platform.dart |
DraftModePlatformStyle, DraftModePlatformScope, and DraftModeUIPlatform style resolution helpers |
— |
package:draftmode_framework/forms.dart |
Form state, validation-aware attributes, formatters, and reusable form widgets | lib/forms/README.md |
package:draftmode_framework/entities.dart |
Reusable field definitions, entity-aware schema fields, and schema validation helpers | — |
package:draftmode_framework/storage.dart |
Storage contracts, adapters, JSON serializers, and typed storage entity managers | lib/storage/README.md |
package:draftmode_framework/repositories.dart |
Typed repository/entity-manager entrypoints for shared, secure, and HTTP-backed persistence | lib/repositories/README.md |
package:draftmode_framework/validation.dart |
Shared validation contracts and issue types for forms, entities, and repositories | — |
Installation #
Add the package to your pubspec.yaml as a path or git dependency and include
Flutter's localization SDK:
dependencies:
draftmode_framework:
path: ../draftmode_framework
flutter_localizations:
sdk: flutter
Run flutter pub get after updating the file.
Localization Setup #
DraftModeFramework bundles only framework-owned localizations: reusable UI defaults
such as dialog action labels and countdown copy. Product, business, and app
content must stay in the consuming application.
The localization layer is maintained source owned by this package. The ARB
files in lib/l10n/ are the string inventory, while the Dart localization
classes are intentionally checked in and reviewed like normal framework code.
Import package:draftmode_framework/localizations.dart and enable the bundled delegates
in your host app:
return MaterialApp(
localizationsDelegates: DraftModeLocalizations.localizationsDelegates,
supportedLocales: DraftModeLocalizations.supportedLocales,
home: const MyHomePage(),
);
The dialog falls back to Yes/No labels (and English countdown text) if the
localization context is missing, which keeps integration tests and minimal host
apps simple.
Global Context Wiring #
Call DraftModeUIContext.init once during bootstrap to register either a
navigatorKey or a root BuildContext. Afterwards you can invoke the dialog
without supplying a context each time:
final navigatorKey = GlobalKey<NavigatorState>();
void main() {
DraftModeUIContext.init(navigatorKey: navigatorKey);
runApp(MyApp(navigatorKey: navigatorKey));
}
Future<void> _delete() async {
final confirmed = await DraftModeUIDialog.show(
title: 'Delete file?',
message: 'This cannot be undone.',
);
if (confirmed == true) {
// Proceed with deletion.
}
}
When neither argument is passed to DraftModeUIDialog.show nor registered via
DraftModeUIContext.init the helper throws a StateError to highlight the
missing context.
Example App #
The /example directory is the reference host app for this package. It wires
DraftModePage, DraftModeUISection, DraftModeUIRow, lists, cards,
buttons, menus, forms, and storage-backed feature flows together so the
library can be evaluated as a coherent app shell instead of isolated widgets.
Use it as both a visual component library and a starting point for the app structure this repository is trying to standardise:
make example-ios-launch-simulator
make example-ios-run
make example-android-launch-simulator
make example-android-run
# legacy aliases kept for backward compatibility
make example-launch-simulator
make example-run
# or manually
cd example
flutter run
Because DraftModeFramework is a library, the actual concrete app realization lives
in example/. That is intentional: the library defines reusable primitives,
while the example shows how they behave in real screens and provides visual
feedback during development.
For card-like surfaces, DraftModeUICard is the semantically opinionated
variant with built-in icon/title/description handling and matching foreground
color overrides, while DraftModeUIInsetCard stays intentionally freeform and
expects all foreground styling to live inside its custom child.
The profile feature also exercises the real storage adapters. It now combines
backend switching, adaptive pull-to-refresh, swipe-to-delete list behavior,
and a typed form workflow on top of the real DraftMode storage/entity-manager
stack.
Shared and secure storage use their package implementations directly, while the
HTTP path uses the real HTTP storage adapter against an injected in-memory
http.BaseClient. That keeps the example close to production wiring without
requiring Docker or an external service during local UI work.
When a feature schema is available, the typed storage entity managers can reuse that same schema to validate decoded entities on load and outgoing entities on save. This keeps form-time and persistence-time validation aligned.
When schemas express date/time relationships, prefer vAfter(...) or
vAfterOrEqual(...) instead of the generic numeric vGreaterThan(...) so the
rule reads like the actual domain constraint.
Within example features, prefer a three-part split when forms describe a domain object:
entities/for the plain data objectschemas/for shared field definitions, defaults, labels, and validatorsbindings/for mapping betweenDraftModeFormStateand the entity
For clonable consumer apps, use the separate draftmode/ci.cd/templates/flutter repository instead of copying example/ directly. Changes to bootstrap rules, recommended folder structure, and LLM guidance should be reviewed for both repositories together. example/ stays framework-owned and may continue to include showcase or validation-specific flows that should not become product defaults.
Assets #
The assets/images/logo.png asset is exported for downstream packages. When
referencing it directly, include the package argument so Flutter looks inside
the dependency bundle:
Image.asset('assets/images/logo.png', package: 'draftmode_framework');
Testing #
Run flutter test --coverage to execute the test suite and verify the
framework keeps a high coverage bar across UI, forms, storage,
entity-manager/repository, and foundation modules.
Contributing #
- Update the framework-owned localization resources in
lib/l10n/when introducing reusable package-level UI strings, while keepinglib/localizations.dartas the stable public facade. - Run
flutter test --coverage. - Submit PRs with a concise summary of behavior changes and test results.
Publish pub.dev #
via Gitlab #
- Login locally to dart
dart pub login - Once logged in, locate your credentials file
- Windows:
%APPDATA%\Dart\pub-credentials.json - Linux/macOS:
~/.config/dart/pub-credentials.json - macOS with homebrew:
~/Library/Application Support/dart
- Copy the contents of this file
- Add Credentials to GitLab
- Goto to your project/group
- Navigate to Settings > CI/CD and expand Variables
- Click Add Variable (Key:
PUB_CREDENTIALS, Value: content of file, Optional: Masked and Protected)
Release #
- Version:
1.1.3 - Image:
not-configured:1.1.3 - Changelog: see CHANGELOG.md
This section is managed by CI.