Getting started
Install the CLI, set up a Flutter app and add a first component. Then theme it, check it on 16 variants and keep it up to date.
Requirements
Flutter 3.47 or later. The CLI runs on the Dart SDK that comes with Flutter, and it checks the Flutter version of your app before it writes anything.
In a git repository, fliin init stops when the tree has uncommitted changes. Commit first, so that you can review what it writes, or pass --allow-dirty.
Install the CLI
Activate the CLI with pub:
dart pub global activate fliinPub puts the command in its cache. If your shell does not find it, add $HOME/.pub-cache/bin to your PATH (%LOCALAPPDATA%\Pub\Cache\bin on Windows), then check that it runs:
fliin versionCreate an app
fliin create runs flutter create, sets Fliin up in the new app, and writes a lib/main.dart whose MaterialApp already wraps every screen in FlScope:
fliin create my_app
cd my_app
flutter runfliin create takes the options of fliin init: --root, --prefix, --preset and --agent.
Or set up an existing app
In the folder of an app you already have:
fliin initItems come from the Fliin registry, signed and checked file by file. To use your own registry instead, pass --registry and --trusted-key.
It writes:
fliin.yaml: where the items go and the prefix of their classes,lib/ui/fliinandFlby default, and the registry they come from.fliin.lock.json: the version and the fingerprint of every installed file, whichfliin updatemerges from. Commit it with the rest.lib/ui/fliin/core/: the theme, its tokens and the shared code that every component imports.AGENTS.md: how your coding agents should use Fliin, described below.- A few lines in
.gitignore, for the files Fliin keeps to itself. lib/ui/fliin/LICENSE: the MIT license of the files Fliin copies, and the notices of the work they derive from.
Then put the theme above your screens. fliin init prints the lines for the builder: argument of your MaterialApp or WidgetsApp:
import 'package:my_app/ui/fliin/core/fliin.dart';
MaterialApp(
builder: (context, child) => FlScope(
child: child ?? const SizedBox.shrink(),
),
home: const HomePage(),
)FlScope hands the tokens to every component and follows the brightness of the platform. Components import only widgets.dart, so they work under MaterialApp, CupertinoApp or WidgetsApp alike.
Add a component
fliin add buttonfliin add downloads the button and what it needs (core is there already, spinner comes along), checks every file against the signed index, writes the imports for your package and your prefix, and copies the files under lib/ui/fliin/components/.
The files are now yours: read them, change them. Use the button like any widget. Here is the lib/main.dart of the app that fliin create my_app made, with a button that shows a spinner while it saves:
import 'package:flutter/material.dart';
import 'package:my_app/ui/fliin/components/button/fl_button.dart';
import 'package:my_app/ui/fliin/core/fliin.dart';
void main() => runApp(const App());
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) => MaterialApp(
builder: (context, child) =>
FlScope(child: child ?? const SizedBox.shrink()),
home: const HomePage(),
);
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
var _saving = false;
Future<void> _save() async {
setState(() => _saving = true);
await Future<void>.delayed(const Duration(seconds: 1));
if (mounted) setState(() => _saving = false);
}
@override
Widget build(BuildContext context) {
final theme = FlScope.of(context);
return ColoredBox(
color: theme.colors.surface,
child: Center(
child: FlButton(label: 'Save', loading: _saving, onPressed: _save),
),
);
}
}To find what to add, fliin search looks items up by what you want to do, fliin list shows what is installed, and fliin view prints the API of an item. The component pages show each one live, with its examples.
fliin search "confirm before deleting"
fliin view buttonTheme it
The Theme page builds a preset: colors, fonts, radius, spacing, shadows, icons and motion, in one short code. Its Get code button gives the command for your app:
fliin theme --preset c05ONTp0000000Or start from your brand color alone. The solver moves lightness in OKLCH until every text and control pair passes WCAG AA in light and dark, or names the nearest color that does:
fliin theme --primary "#7C3AED"Both rewrite core/tokens/fliin.tokens.json and regenerate tokens.g.dart from it, all or nothing: do not edit the generated file by hand. When a preset names a font, the command tells you what to declare in pubspec.yaml. A new app can start from a preset with fliin create --preset.
Check it on 16 variants
fliin verify buttonfliin verify renders the item inside your app, on iOS and Android, light and dark, text at 1.0× and 2.0×, left to right and right to left. It writes the 16 images, a contact sheet and a report to build/fliin/verify/button/, and fails when a variant does not render. When your theme, Flutter version and fonts are those of the published reference renders, it also measures how far your copy is from them.
After an edit, check what it touches, the items that depend on it included:
fliin verify --changedUpdate without losing your edits
fliin update --check
fliin diff button
fliin updatefliin update --check lists the items that have a new version, and fliin diff shows what changes upstream. fliin update then merges each file three ways: the version you installed, your copy and the new version. Your edits stay, upstream fixes come in.
When you and upstream changed the same lines, your file stays as it is and the new version goes next to it, in fl_button.dart.fliin-new: the build does not break, and the item keeps its version. Carry over what you want from it, delete the .fliin-new file, then run fliin update again to finish.
fliin update never deletes a file of yours and never moves an item back to an older version. With --conflict=markers, it writes conflict markers in the file instead, as git does.
Work with your agents
fliin init writes AGENTS.md: the loop your coding agents should follow, and the rules for the code under lib/ui/fliin. It also connects the Fliin MCP server to the agents it finds traces of in the project, such as CLAUDE.md, .cursor/ or .codex/. To name them, at setup or later:
fliin init --agent claude,cursor
fliin agents --agent codexThe clients it knows: claude, cursor, gemini, codex, vscode, android-studio. With --agent none, fliin init writes no agent file.
Your agent then starts fliin mcp, a local server with three tools: search_items finds items by intent, get_item returns the files, API and examples of an item, and request_selection opens a page on your machine where you choose between candidates. Your code and your paths never leave your machine: the CLI only downloads files from the registry.
An agent without MCP can read llms.txt, and every item page has a Markdown version next to it.
Fliin Pro
Blocks and screens marked Pro need a license, bought once on the Fliin Pro page. Add your license key once per machine, then install Pro items like any other:
fliin login
fliin add account-settingsfliin login asks for the key without showing it, and keeps it outside your project, in ~/.config/fliin/credentials.json (%APPDATA%\fliin\ on Windows). In CI, set FLIIN_LICENSE_KEY instead. fliin status shows the active license, and fliin logout removes it.
Without a license, fliin add of a Pro item stops with exit code 8 and says where to get one. The Pro items you installed keep working, and fliin update brings their new versions for as long as Fliin Pro exists.
Where next
- Components: every component with its live preview, examples, API and reference renders.
- Blocks and Screens: sections and whole screens with their states, strings and routes. Some blocks and screens are part of Fliin Pro.
- Theme: build a preset and see it on every component.