lark 0.1.1 copy "lark: ^0.1.1" to clipboard
lark: ^0.1.1 copied to clipboard

Platformweb

A light, elegant Dart web framework with file-based routing, reactive state, and SSR/SSG support.

Lark #

Lark is a Dart web framework with:

  • file-based routing
  • fine-grained reactivity (ref, computed, effect)
  • composables for async/data/storage
  • static pre-rendering (SSG)
  • server-side rendering (SSR)
  • a built-in CLI for scaffolding, dev, and production builds

Layering Model #

Lark separates core behavior and styled UI into two layers:

  • lark (package:lark/lark.dart) for headless, unstyled primitives and behavior.
  • lark_ui (package:lark_ui/lark_ui.dart) for opinionated, theme-aware UI components.

Canonical boundary spec: ../../docs/5.architecture/2.boundary.md

Install #

dart pub add lark

Quick Start #

Create a project:

dart run lark create my_app
cd my_app
dart pub get
lark dev

Open http://localhost:3000.

App Entry #

lib/app.dart:

import 'package:lark/lark.dart';
import 'routes.g.dart';

final app = LarkApp(
  routes: routes,
  config: const LarkConfig(name: 'My App'),
  head: const Head(title: 'My App'),
);

web/main.dart:

import 'package:lark/lark.dart';
import 'package:my_app/app.dart';

void main() {
  createApp(app).mount('#app');
}

Dark Mode Setup #

Set the Tailwind strategy in lib/app.dart (defaults to selector mode):

final config = LarkConfig(
  name: 'My App',
  tailwind: TailwindConfig(darkMode: DarkMode.selector),
);

Use dark: classes in UI and control preference with useDarkMode():

final theme = useDarkMode();
theme.toggle();      // switch dark/light
theme.setSystem();   // follow OS preference

On web, Lark automatically wires storage and system preference detection when the app mounts.

Reactivity #

import 'package:lark/lark.dart';

class CounterPage extends Component {
  final count = ref(0);
  late final doubled = computed(() => count() * 2);

  @override
  Component build() {
    return Column([
      Text('Count: ${count()}'),
      Text('Doubled: ${doubled()}'),
      Button(label: 'Increment', onPressed: () => count.value++),
    ]);
  }
}

Routing #

File-based routing #

Lark generates lib/routes.g.dart from lib/pages:

  • lib/pages/index.dart -> /
  • lib/pages/about.dart -> /about
  • lib/pages/blog/__slug__/index.dart -> /blog/:slug

Routes are regenerated by lark dev, lark build, and lark codegen.

Runtime router APIs #

  • useRouter() for push, replace, back, forward, go
  • useRoute() for current path/params/query/meta/data
  • useParams() and useQueryParams() helpers
  • RouterLink for SPA navigation
  • RouterOutlet for nested route trees

State, Forms, and Composables #

Store #

final useCounterStore = defineStore('counter', () {
  final count = ref(0);
  final doubled = computed(() => count() * 2);

  void increment() => count.value++;

  return (count: count, doubled: doubled, increment: increment);
});

Form #

final form = useForm({
  'email': field(
    initialValue: '',
    validators: [required, email],
    formatters: [trim, lowercase],
  ),
  'password': field(validators: [required, minLength(8)]),
  'confirmPassword': field(initialValue: ''),
}, validators: [
  fieldsMatch('password', 'confirmPassword', message: 'Passwords must match'),
], autovalidateMode: AutovalidateMode.onUserInteraction);

final email = form.bindTextField('email', validateOnBlur: true);

Future<void> submit() async {
  final ok = await form.submit((values) async {
    await api.register(values);
  }, focusFirstError: true, scrollToFirstError: true);
  if (!ok) {
    print(form.errors);
    print(form.formError);
    print(form.submitError);
  }
}

Input.email(
  value: email.valueRef,
  onChanged: email.onChanged,
  onFocus: email.onFocus,
  onBlur: email.onBlur,
  onSubmit: email.onSubmit,
  textInputAction: TextInputAction.next,
);

final schemaForm = useSchemaForm({
  'fields': [
    {'name': 'email', 'validators': ['required', 'email']},
  ],
);

Overlays (Dialogs, Drawers, Popovers) #

final open = ref(false);

Dialog(
  open: open,
  trigger: Button(label: 'Open settings'),
  content: DialogContent([
    DialogHeader([DialogTitle('Settings')]),
    DialogFooter([DialogClose(Button(label: 'Close'))]),
  ]),
  dismissOnEscape: true,
  dismissOnOutsideTap: true,
  trapFocus: true,
  lockScroll: true,
);

ScrollView Primitive #

final scroll = ScrollController();

ScrollView(
  controller: scroll,
  axis: ScrollAxis.vertical, // vertical | horizontal | both
  showScrollbar: true,
  onScroll: (x, y) {
    // sync sticky UI, load-more signals, etc.
  },
  child: Column([
    Text('Scrollable content'),
  ]),
);

scroll.scrollToBottom();
scroll.scrollTo(y: 240, smooth: true);

Theme Tokens + DOM Helpers #

final theme = Theme.light.copyWith(
  colors: Theme.light.colors.copyWith(primary: Color.hex('#2563eb')),
);
setTheme(theme, syncToDom: true);
final tailwindTokens = theme.toTailwindThemeExtension();

final dom = useDom();
dom.scrollToId('first-error', offset: 72);
dom.focusById('email');

Built-in composables #

  • useFetch, useLazyFetch, usePost
  • usePagination, useInfiniteScroll, useAsync, usePolling
  • useToggle, useDebounce, useThrottle, usePrevious
  • useLocalStorage, useSessionStorage, useCookie, useDarkMode, useDom
  • useQuery, useMutation, useLazyQuery

SEO and Head Tags #

  • useMeta(...)
  • useSeo(...)
  • useHead([...])
  • app-level Head(...) on LarkApp

Content System #

Put content files in lib/content (.md, .yaml, .json) and run codegen.

Generated catalog: lib/content/content.g.dart

final posts = await content.query('blog').search('dart').find();
final one = await content.findOne('my-post-slug');

CLI #

lark create <name>
lark dev [--port] [--verbose] [--ssr]
lark build [--wasm | --js]
lark start
lark serve [--port] [--host] [--dir]
lark generate <component|page|store> <name>
lark codegen
lark doctor
lark clean
lark tailwind setup

Pub Score Check #

Run the local pub score check with the included helper:

./tool/pana_check.sh

This script currently forces --dartdoc-version "<9.0.0" as a temporary workaround for a pana/dartdoc compatibility issue on Dart 3.11.

Deployment #

Static (SPA + pre-rendered pages) #

lark build

Deploy build/ to static hosting.

Local check:

lark serve

SSR #

lark build also compiles an SSR server binary:

lark build
lark start

lark start runs build/server (host/port from args or .env).

For source-mode SSR in environments with Dart installed:

cd build
dart pub get
dart run bin/server.dart

Split Imports #

Use smaller entrypoints when needed:

  • package:lark/core.dart
  • package:lark/router.dart
  • package:lark/store.dart
  • package:lark/seo.dart
  • package:lark/composables.dart
  • package:lark/ssr.dart
  • package:lark/web.dart
1
likes
160
points
185
downloads

Publisher

unverified uploader

Weekly Downloads

A light, elegant Dart web framework with file-based routing, reactive state, and SSR/SSG support.

Homepage
View/report issues

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

args, markdown, meta, path, vm_service, web, yaml

More

Packages that depend on lark