Built-in Template Events
Finch injects a set of built-in variables and helper functions into every Jinja template. These are accessible via special prefixes alongside your own addParam() data.
Global Variables
{{ isLocalDebug }} {# true when running in debug mode #}
{{ data }} {# map of all params added via rq.addParam() #}
{{ session }} {# server-side session map #}
{{ $rq }} {# the Request object #}
Asset Helpers
{{ assets.js() }} {# renders all <script> tags added via rq.addAsset() #}
{{ assets.css() }} {# renders all <link> tags #}
{{ assets.dataJs() }} {# renders data attributes for JS access #}
Route and URL Helpers ($e)
{{ $e.route }} {# the matched route's rendered path, e.g. '/users/42' — the specific path/extraPath that matched this request #}
{{ $e.routePath }} {# the route's canonical full path pattern, e.g. '/users/{id}' (always via the primary `path`, even if an `extraPath` is what matched) #}
{{ $e.routeKey }} {# the route's `key`, e.g. 'users.show' — NOT the same as $e.route #}
{{ $e.isKey('root.panel') }} {# true if current route's key matches #}
{{ $e.hasKey(['root.panel', 'root.form']) }} {# true if current route's key is one of the keys #}
{{ $e.routeUrl('key') }} {# URL for a named route #}
{{ $e.routeUrl('users.show', {'id': '42'}) }} {# URL with path params #}
{{ $e.routeUrl('search', {}, {'q': 'hello'}) }} {# URL with query params #}
{{ $e.uri }} {# full request URI, percent-encoded as a single string #}
{{ $e.uriString }} {# full request URI, NOT encoded #}
{{ $e.path }} {# request path only, percent-encoded as a single string #}
{{ $e.pathString }} {# request path only, NOT encoded #}
{{ $e.isPath('/example/form') }} {# true if current path matches #}
{{ $e.endpoint }} {# the matched endpoint path #}
{{ $e.url('/about') }} {# build an absolute URL from a path #}
{{ $e.urlParam('/users', {'id': '5'}) }} {# URL with appended query params #}
{{ $e.urlToLanguage('fa') }} {# current URL switched to another language #}
Despite the names,
$e.uri/$e.pathare percent-encoded strings, not a DartUriobject or a list of path segments — use$e.uriString/$e.pathStringwhen you need the raw, unencoded value.
Cookie Helpers
{{ $e.getCookie('theme', 'light') }} {# read a cookie with default value; always a plain (unencrypted) read #}
Language Helpers
{{ $e.ln }} {# current language code, e.g. 'en' #}
{{ $e.langs }} {# list of all configured languages: [{code, label, contry}] #}
$e.dir and the label/contry fields inside each $e.langs entry are not read from a plain "dir" key in your language file — they look up dedicated translation keys named language.<code>_dir, language.<code>_label, and language.<code>_contry (note: contry, not country — this is a real spelling quirk in the framework, not a typo in this doc). If those keys don't exist in your language files, .tr.write() silently falls back to returning the key itself, so {{ $e.dir }} would literally render the text language.en_dir instead of ltr.
The Finch example project's own language files only define a plain "dir" key and don't define these language.* keys, so $e.dir/$e.langs[].label/$e.langs[].contry render unresolved out of the box. The example project's own layout works around this by using {{ $t('dir') }} instead (a direct lookup of the plain "dir" key) — see the Templates complete example. Either add the language.<code>_dir/_label/_contry keys to every language file yourself, or prefer $t('dir') / your own translation keys over $e.dir and the label/contry fields of $e.langs.
Utility Helpers
{{ $e.widgetPath('partials/nav') }} {# appends the configured widget extension to a path, e.g. 'partials/nav.j2.html' — does not prepend widgetsPath #}
{{ $e.randomString(8) }} {# random string of 8 characters (default length is 4 if omitted) #}
{{ $e.toString(value) }} {# coerce any value to string #}
Translation
{{ $t('logo.title') }} {# translate a key #}
{{ $t('greeting', {'name': user.name}) }} {# translate with parameters #}
Nested Data Navigation
{{ $n('user/address/city', 'Unknown') }} {# navigate nested params safely #}
Debug Dump
{{ dump(data) }} {# dump any variable visually in the browser (debug only) #}
Custom Local Events
Define your own global functions in app.dart using Request.localEvents:
Request.localEvents.addAll({
'currentYear': () => DateTime.now().year,
'appName': () => 'My App',
});
Access them in templates with the $l. prefix:
<footer>© {{ $l.currentYear() }} {{ $l.appName() }}</footer>