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.

pub version license: MIT platform: Android | iOS | Web Dart SDK: >=3.11 <4 Flutter: >=3.41 docs


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.


Requirements

  • Flutter >=3.41.0 (stable or equivalent; matches meye’s pubspec and transitive plugins such as pro_image_editor 12.x).
  • Dart >=3.11.0 <4.0.0 (bundled with the Flutter SDK above).

Use flutter --version to confirm before adding meye or running create_app.


Features

  • Config-driven UI engine — describe screens in JSON; MiFactory maps "component" keys to 60+ built-in widgets including lists, cards, forms, tabs, maps, carousels, charts, and chat layouts
  • Declarative action pipeline — 40+ built-in actionType processors (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) — orchestrates AppStatus, optional AuthStatus, and optional LocationStatus; gates rendering with MiAppWrapper and MiAuthWrapper
  • go_router integrationMiRouter provides 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 themeData map via ITheme
  • App config merge/split tooling — Python scripts merge per-route page.json files into a unified config and regenerate the Dart constant (app_config.g.dart) on every save
  • create_app CLI — interactive wizard (or --no-interactive flags) 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

Use the latest 3.x line from pub.dev (adjust if you need an older major):

# pubspec.yaml
dependencies:
  meye: ^3.1.2

Or via the command line:

flutter pub add meye

Ensure your app’s SDK constraint allows Dart >=3.11.0 and that you are on Flutter >=3.41.

The fastest path is the create_app CLI, which generates and fully wires a new Flutter app for you.

From a local checkout (run from the cloned meye repository root):

dart run meye:create_app

From pub.dev (global install):

# Latest published version
dart pub global activate meye

# Or pin a specific version (recommended for reproducible CI/scripts)
dart pub global activate meye 3.1.2

Then run the executable as create_app (or dart pub global run meye:create_app).
If create_app is not found, add Pub’s global bin directory to your PATH (see dart pub global activate output, often ~/.pub-cache/bin).

See CLI Reference below for all flags and options.

More detail: docs/create_app.md.

If global create_app ever fails to find the package root, upgrade to meye ≥3.1.1 or set MEYE_PACKAGE_ROOT to the folder that contains meye’s pubspec.yaml.


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. Before scaffolding, it also preflights the Android NDK: if the Flutter-pinned NDK is missing or incomplete, it prints exact sdkmanager paths (under your SDK’s cmdline-tools) so copy/paste works without putting sdkmanager on PATH. See docs/create_app.md. 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

Use Flutter >=3.41 and Dart >=3.11 (see Requirements). Contributions are welcome! Please follow these steps:

  1. Fork the repository and clone your fork locally.
  2. Create a feature branch from main:
    git checkout -b feat/my-new-feature
    
  3. Make your changes, following Effective Dart style guidelines throughout.
  4. Run the tests before opening a PR:
    flutter test
    
  5. Run the linter and fix any issues:
    flutter analyze
    
  6. Commit with a clear, imperative-mood message (e.g. Add MiVideoPlayer component).
  7. 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_lints rules (enforced by flutter 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 (e.g. >=3.41), Dart, 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

Libraries

meye