meye 1.0.0
meye: ^1.0.0 copied to clipboard
Core Flutter package for config-driven UI, app lifecycle orchestration, and go_router integration in host applications.
meye #
A config-driven Flutter app framework that turns JSON into full-featured screens — complete with routing, theming, auth, and an action engine — scaffolded in one command.
Overview #
meye is a host-embedded Flutter/Dart package that provides a complete runtime framework for building config-driven mobile and web applications. Rather than writing bespoke widget trees for every screen, you describe your UI in JSON and let the engine render, bind, and respond to user interactions automatically.
Host applications provide concrete repository implementations (auth, API, user, theme, etc.). The framework handles the rest: app lifecycle, routing with go_router, authentication gating, data binding with ${...} placeholders, and a declarative action pipeline.
A companion create_app CLI scaffolds a fully-wired host app in a single command so you can be productive from minute one.
Full component and guide documentation is available at docs.misrut.com.
Features #
- Config-driven UI engine — describe screens in JSON;
MiFactorymaps"component"keys to 60+ built-in widgets including lists, cards, forms, tabs, maps, carousels, charts, and chat layouts - Declarative action pipeline — 40+ built-in
actionTypeprocessors (apiCall,navigate,validateForm,showBottomSheet,captureLocation,print,scan, and more); compose them sequentially in JSON ${...}data binding — placeholders resolve against live page data, global state (global.*), and secure storage (storage.*) at runtime- Conditional rendering — built-in condition operators (
equalsTo,in,isNull,platform,and/or, etc.) enable platform-aware and data-driven branching directly in config - App lifecycle bloc (
AppBloc) — orchestratesAppStatus, optionalAuthStatus, and optionalLocationStatus; gates rendering withMiAppWrapperandMiAuthWrapper go_routerintegration —MiRouterprovides dynamic route patterns (/:route1/…/:route4), auth redirect hooks, and extension points (extraRoutes,wrapperBuilder,routeInit)- Config-driven theming — define your entire Material 3 colour palette, text styles, and per-component overrides in a JSON
themeDatamap viaITheme - App config merge/split tooling — Python scripts merge per-route
page.jsonfiles into a unified config and regenerate the Dart constant (app_config.g.dart) on every save create_appCLI — interactive wizard (or--no-interactiveflags) that scaffolds, patches, and wires a production-ready host app including NDK pinning, desugaring, iOS deployment target, and minSdk derivation
Installation #
Adding to an existing host app #
# pubspec.yaml
dependencies:
meye: ^0.0.1
Or via the command line:
flutter pub add meye
Scaffolding a new host app (recommended) #
The fastest path is the create_app CLI, which generates and fully wires a new Flutter app for you.
From a local checkout:
dart run meye:create_app
From pub.dev (global install):
dart pub global activate meye
create_app
See CLI Reference below for all flags and options.
Platform-specific setup #
Because meye bundles a rich set of native plugins (camera, location, notifications, secure storage, contacts, etc.), each platform requires additional configuration. The create_app CLI handles Android NDK pinning, desugaring, and minSdk patching automatically. The iOS Podfile and deployment target are also patched automatically. Manual additions required for each feature you use are listed below.
Android
Add the following to android/app/src/main/AndroidManifest.xml as needed:
<!-- Location -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Camera / Image Picker -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- Notifications (Android 13+) -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- Microphone / Audio Recording -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Contacts -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
Ensure minSdk is at least 21. The CLI derives and sets the correct value from resolved plugins automatically.
iOS
Add the following keys to ios/Runner/Info.plist as needed:
<!-- Location -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app uses your location to …</string>
<!-- Camera -->
<key>NSCameraUsageDescription</key>
<string>This app uses the camera to …</string>
<!-- Photo Library -->
<key>NSPhotoLibraryUsageDescription</key>
<string>This app accesses your photo library to …</string>
<!-- Microphone -->
<key>NSMicrophoneUsageDescription</key>
<string>This app uses the microphone to …</string>
<!-- Contacts -->
<key>NSContactsUsageDescription</key>
<string>This app accesses your contacts to …</string>
The CLI sets the iOS deployment target to 15.0 (or higher if plugins require it) in ios/Podfile automatically.
Usage #
CLI Reference #
Interactive mode (default)
dart run meye:create_app
The wizard prompts for:
| Prompt | Constraint | Default |
|---|---|---|
| Project name | ^[a-z][a-z0-9_]*$, not a Dart reserved word |
(required) |
| Output directory | Absolute or relative path | ./<project_name> |
| Organization | Reverse domain notation, e.g. com.example |
com.example |
| Include auth scaffolding? | y / n |
n |
Non-interactive mode
dart run meye:create_app \
--no-interactive \
--name my_app \
--out ./my_app \
--org com.example \
--auth \
--platforms android,ios
All flags
| Flag | Description |
|---|---|
--name <pkg> |
Package name (snake_case) |
--out <dir> |
Output directory |
--org <org> |
Reverse domain org identifier |
--auth |
Include auth scaffolding (login/signup + repository stubs) |
--platforms <csv> |
Platforms forwarded to flutter create (e.g. android,ios,web) |
--android-min-sdk <int> |
Override derived Android minSdk |
--local <path> |
Use a local path dependency to meye instead of pub.dev |
--no-interactive |
Skip prompts; all inputs come from flags |
What gets generated #
Always generated:
| Path | Purpose |
|---|---|
lib/main.dart |
App entry point wired to AppBloc and MiRouter |
lib/app.dart |
MaterialApp.router setup |
lib/router/router.dart |
MiRouter subclass with your routes |
lib/theme/theme.dart |
ITheme subclass with a blue/white Material 3 palette |
lib/app_config/app_config.json |
App-level config (service key, drawer, route order) |
lib/app_config/routes/home/page.json |
Home page UI config |
lib/app_config/generated/app_config.json |
Merged unified config (auto-regenerated) |
lib/app_config/generated/app_config.g.dart |
Dart string constant from merged JSON |
scripts/app_config.py |
Merge/split tool |
scripts/watch_app_config.py |
File-watcher: auto-merges and regenerates on save |
Auth variant additionally generates:
| Path | Purpose |
|---|---|
lib/screens/auth/LoginPage/LoginPage.dart |
Login screen |
lib/screens/auth/LoginPage/LoginCubit.dart |
Login state management |
lib/repositories/MyAuthRepository.dart |
IAuthRepository stub |
lib/repositories/MyUserRepository.dart |
IUserRepository stub |
lib/repositories/MyAPIRepository.dart |
APIRepository stub |
App config structure #
App configuration lives in lib/app_config/ and follows a split layout: one app_config.json for top-level config and one page.json per route under routes/.
lib/app_config/app_config.json
{
"service": "my_app",
"feature": "baseApp",
"platform": "android",
"data": {
"routesOrder": ["/home", "/profile"],
"drawer": {
"title": "My App",
"tiles": [
{ "label": "Home", "icon": "home", "route": "/home" },
{ "label": "Profile", "icon": "person", "route": "/profile" }
]
}
}
}
lib/app_config/routes/home/page.json
{
"page": {
"component": "defaultPageLayout",
"actions": { "init": [] },
"header": {
"component": "header",
"title": "Home",
"leading": { "component": "drawerMenuButton" }
},
"layoutConfig": "1",
"body": [
{
"component": "text",
"text": "Welcome, ${global.user.name}!",
"style": "headlineMedium",
"color": "primary"
}
]
}
}
Route directories map directly to URL paths. Dynamic segments use square-bracket notation:
| File path | URL route |
|---|---|
routes/home/page.json |
/home |
routes/profile/page.json |
/profile |
routes/orders/[id]/page.json |
/orders/:id |
Regenerating app config #
After editing any page.json or app_config.json:
# One-shot merge and regenerate
python scripts/app_config.py merge
# Watch mode: auto-runs on every .json save (requires: pip install watchdog)
python scripts/watch_app_config.py
The generated app_config.g.dart is committed to source control so the project compiles without Python installed.
Theming #
Override ITheme.themeData in lib/theme/theme.dart to customise your app's appearance:
{
"colors": {
"primary": "#1565C0",
"onPrimary": "#FFFFFF",
"secondary": "#455A64"
},
"textStyles": {
"headlineMedium": { "size": 28, "weight": 400, "letterSpacing": 0 }
},
"components": {
"button": { "borderRadius": 8 },
"textField": { "fontSize": 14, "borderRadius": 8, "labelBehaviour": "inline" },
"header": { "titleColor": "secondary", "titleStyle": "headlineMedium" }
}
}
ThemeRepository derives a full Material 3 ColorScheme from the colors map, with safe fallbacks for any omitted keys. Call ThemeRepository.setTheme(newThemeData) at runtime to apply server-side theme overrides instantly.
Built-in page components #
MiFactory resolves the "component" key in any config object to one of 60+ built-in widgets. The complete reference — including every prop, data-binding key, and usage example for each component — is available at docs.misrut.com/components.
A quick reference of the most commonly used components:
| Component key | Description | Docs |
|---|---|---|
defaultPageLayout |
Standard page with header, body, and optional drawer | ↗ |
listView |
Scrollable list with index-aware ${[index]} bindings |
↗ |
smallCard, largeCard, groupCard |
Card variants | ↗ |
button |
Tap target with configurable action |
↗ |
textFormField, searchField |
Input fields with inline/float label behaviour | ↗ |
dropdown, checkbox, toggle, radio |
Form controls | ↗ |
column, row, flexible |
Layout primitives | ↗ |
text, icon, divider, spacing |
Display & spacing | ↗ |
networkImage, imageCarousel, imagePicker |
Image components | ↗ |
table, reorderList |
Data display | ↗ |
selectFromListButton |
Searchable list picker | ↗ |
datePicker, timePicker |
Date & time inputs | ↗ |
slider, aspectRatio, pageLayout |
Misc layout helpers | ↗ |
miCondition |
Inline conditional rendering | ↗ |
responsiveBuilder |
Platform-adaptive layout | ↗ |
cardWithBgPageLayout |
Full-bleed background card layout | ↗ |
Built-in actions #
Declare actions in any page.json under "actions" or inline on a component's "action" key:
{
"actions": {
"init": [
{
"actionType": "apiCall",
"data": { "userId": "${global.user.id}" },
"stateKey": "orders"
}
],
"submitOrder": [
{ "actionType": "validateForm" },
{ "actionType": "apiCall", "data": "${root}" },
{ "actionType": "navigate", "navigationType": "pop" }
]
}
}
A selection of built-in actionType values:
actionType |
Description |
|---|---|
apiCall |
POST request via APIRepository; writes response to state |
navigate |
go, push, replace, pop via go_router |
validateForm |
Validates all form fields on the page |
updateData |
Mutates page-local state data |
showBottomSheet / hidePopup |
Modal overlay management |
captureLocation |
Reads GPS via ILocationRepository |
signIn / signOut |
Delegates to IAuthRepository |
scan |
QR/barcode scanner |
print |
PDF generation and printing |
share |
Native share sheet |
openWebView |
In-app web view |
readDeviceInfo |
Reads device metadata |
timer |
Delayed or repeating action |
stringManipulator, math, listManipulator |
Data transformation utilities |
showSnackbar, throwSnackBarError |
Toast/snackbar feedback |
Extending the framework #
Custom components: Register additional widget builders in AppRepository:
AppRepository(
customComponents: { 'myWidget': MyWidgetBuilder() },
newBuilders: { 'myCard': (config) => MyCard(config) },
)
Custom actions: Add your own MiAction processors:
AppRepository(
customActions: { 'myAction': MyCustomAction() },
)
Custom routes: Override MiRouter.extraRoutes in lib/router/router.dart:
@override
List<GoRoute> get extraRoutes => [
GoRoute(path: '/settings', builder: (_, __) => const SettingsPage()),
];
Contributing #
Contributions are welcome! Please follow these steps:
- Fork the repository and clone your fork locally.
- Create a feature branch from
main:git checkout -b feat/my-new-feature - Make your changes, following Effective Dart style guidelines throughout.
- Run the tests before opening a PR:
flutter test - Run the linter and fix any issues:
flutter analyze - Commit with a clear, imperative-mood message (e.g.
Add MiVideoPlayer component). - Push your branch and open a Pull Request against
main. Fill in the PR template with a summary of the change, the motivation, and any relevant screenshots.
Please ensure:
- New components include at least one unit or widget test.
- Public API additions are documented with doc comments.
- No new
print()statements are introduced in library code. - Code follows
flutter_lintsrules (enforced byflutter analyze).
Additional information #
- Documentation: Full component reference, guides, and examples at docs.misrut.com.
- Issues & bug reports: Open an issue on the repository. Please include Flutter version, platform, and a minimal reproduction.
- Feature requests: Open a discussion or issue with the label
enhancement. - License: MIT — see LICENSE for full terms.
Made with ❤️ by Misrut