A lightweight, modern, and powerful Flutter framework that combines state management, dependency injection, and route management in a clean, performant, and easy-to-use API. Inspired by GetX but with a cleaner design focused on performance and simplicity.
- Features
- Installation
- Quick Start
- Architecture
- Core Concepts
- API Reference
- Migration Guide from GetX
- Performance Tips
- Example App
- Acknowledgments
- License
- Dependency Injection: Simple and powerful DI with
Flow.put,Flow.find, andFlow.isRegistered - Reactive State Management: Ultra-fast reactive programming with
Rxtypes andFlxwidgets - Route Management: Clean navigation with
Flow.to,Flow.toNamed, andFlow.back - Logic/State/View Pattern: Clear separation of concerns for maintainable code
- Zero Boilerplate: No need for StreamControllers, ChangeNotifiers, or InheritedWidgets
- Performance Optimized: Single-level observation design for maximum performance
- Type-Safe: Full Dart type system support
- Multi-Platform: Works on Android, iOS, Web, Windows, macOS, and Linux
Add this to your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
liteflows: ^0.0.2Then run:
flutter pub getImport the package:
import 'package:liteflows/flows.dart';Here's a complete example of a counter app:
import 'package:flutter/material.dart';
import 'package:liteflows/liteflows.dart';
// 1. Define State
class CounterState {
final count = 0.obs;
}
// 2. Define Logic
class CounterLogic extends FlowController {
CounterLogic() {
state = CounterState();
}
void increment() => state.count.value++;
void decrement() => state.count.value--;
void reset() => state.count.value = 0;
}
// 3. Define View
class CounterPage extends StatelessWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context) {
// Register logic if not already registered
if (!Flow.isRegistered<CounterLogic>()) {
Flow.put(CounterLogic());
}
final logic = Flow.find<CounterLogic>();
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Reactive text that updates automatically
Flx(() => Text(
'Count: ${logic.state.count.value}',
style: const TextStyle(fontSize: 32),
)),
const SizedBox(height: 32),
// Buttons with reactive handlers
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: const Icon(Icons.remove_circle_outline),
onPressed: logic.decrement,
),
IconButton(
icon: const Icon(Icons.add_circle_outline),
onPressed: logic.increment,
),
IconButton(
icon: const Icon(Icons.refresh),
onPressed: logic.reset,
),
],
),
],
),
),
);
}
}
// 4. Define App
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return FlowMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flows Demo',
pages: [
FlowPage(name: '/counter', page: () => const CounterPage()),
],
initialRoute: '/counter',
);
}
}
void main() {
runApp(const MyApp());
}Flows uses a Logic/State/View architecture pattern that provides clear separation of concerns:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ View │────▶│ Logic │────▶│ State │
│ (Widgets) │◀────│ (Business) │◀────│ (Data) │
└─────────────┘ └─────────────┘ └─────────────┘
- State: Holds reactive data (
Rxtypes) - Logic: Contains business logic and state mutations
- View: Displays state and dispatches actions to logic
This pattern provides:
- Clear separation of concerns
- Easy testing of business logic
- Reusable state and logic
- Better code organization
Flows provides a simple yet powerful dependency injection system:
// Register a dependency
Flow.put(MyService());
// Register with a name
Flow.put(MyService(), name: 'myService');
// Register as lazy (created on first access)
Flow.putLazy(() => MyService());
// Find a dependency
final service = Flow.find<MyService>();
// Find by name
final service = Flow.find<MyService>(name: 'myService');
// Check if registered
if (Flow.isRegistered<MyService>()) {
// Do something
}
// Remove a dependency
Flow.remove<MyService>();
// Remove all dependencies
Flow.disposeAll();FlowController provides lifecycle methods:
class MyLogic extends FlowController {
@override
void onInit() {
super.onInit();
// Called when controller is created
}
@override
void onClose() {
// Called when controller is disposed
super.onClose();
}
}Flows uses reactive types that automatically update the UI when changed:
// Define reactive state
class MyState {
final count = 0.obs; // Reactive int
final name = ''.obs; // Reactive String
final items = RxList<String>([]); // Reactive List
final user = Rxn<User>(); // Reactive nullable object
final config = RxMap(); // Reactive Map
final isActive = false.obs; // Reactive bool
}The Flx widget rebuilds when observed state changes:
// Full rebuild of widget tree
Flx(() => Text('Count: ${logic.state.count.value}'));
// FlxValue for single value optimization
FlxValue(
logic.state.count,
(count) => Text('Count: $count'),
);| Type | Description | Example |
|---|---|---|
Rx<T> |
Reactive wrapper for any type | Rx<int>(0) |
Rxn<T> |
Reactive nullable wrapper | Rxn<User>() |
RxBool |
Reactive bool | false.obs |
RxInt |
Reactive int | 0.obs |
RxDouble |
Reactive double | 0.0.obs |
RxString |
Reactive String | ''.obs |
RxList<T> |
Reactive List | RxList<String>([]) |
RxMap<K, V> |
Reactive Map | RxMap() |
RxSet<T> |
Reactive Set | RxSet() |
Flows provides simple navigation APIs:
// Navigate to named route
Flow.toNamed('/detail');
// Navigate with arguments
Flow.toNamed('/detail', arguments: {'id': 123});
// Navigate with custom route
Flow.to(NextPage());
// Navigate with custom route and arguments
Flow.to(NextPage(), arguments: {'data': myData});
// Go back
Flow.back();
// Go back with result
Flow.back({'result': 'success'});
// Replace current route
Flow.off(NextPage());
// Replace all routes
Flow.offAll(NextPage());FlowMaterialApp(
debugShowCheckedModeBanner: false,
title: 'My App',
// Light theme
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
),
// Dark theme
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.dark,
),
),
themeMode: ThemeMode.system,
// Define routes
pages: [
FlowPage(name: '/home', page: () => HomePage()),
FlowPage(name: '/detail', page: () => DetailPage()),
],
initialRoute: '/home',
)View-level (Recommended):
class DetailPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Get arguments from route settings
final args = ModalRoute.of(context)?.settings.arguments;
final id = args is Map ? args['id'] as int? : null;
final data = args is Map ? args['data'] : null;
// Use id and data...
}
}| Method | Description |
|---|---|
Flow.put<T>(instance) |
Register a dependency |
Flow.putLazy<T>(factory) |
Register lazy dependency |
Flow.find<T>(name) |
Find a dependency |
Flow.isRegistered<T>(name) |
Check if registered |
Flow.remove<T>(name) |
Remove a dependency |
Flow.disposeAll() |
Remove all dependencies |
| Method | Description |
|---|---|
onInit() |
Called on controller creation |
onClose() |
Called on controller disposal |
| Constructor | Description |
|---|---|
T.obs |
Extension to create reactive type |
Rx<T>(value) |
Create reactive wrapper |
Rxn<T>() |
Create nullable reactive wrapper |
RxList<T> constructor |
Create reactive List |
| Widget | Description |
|---|---|
Flx(builder) |
Reactive widget builder |
FlxValue(rx, builder) |
Optimized single-value reactive widget |
| Method | Description |
|---|---|
Flow.to(page) |
Navigate to page |
Flow.toNamed(name) |
Navigate to named route |
Flow.back() |
Go back |
Flow.off(page) |
Replace current route |
Flow.offAll(page) |
Replace all routes |
Flows is inspired by GetX but with a cleaner API. Here's how to migrate:
| GetX | Flows |
|---|---|
Get.put() |
Flow.put() |
Get.find() |
Flow.find() |
Get.isRegistered() |
Flow.isRegistered() |
Get.delete() |
Flow.remove() |
Get.to() |
Flow.to() |
Get.toNamed() |
Flow.toNamed() |
Get.back() |
Flow.back() |
Get.off() |
Flow.off() |
Get.offAll() |
Flow.offAll() |
GetX<Controller> |
Flx(() => ...) |
Obx(() => ...) |
Flx(() => ...) |
RxInt, RxString, etc. |
Same with .obs extension |
GetMaterialApp |
FlowMaterialApp |
GetPage |
FlowPage |
GetX:
class Controller extends GetxController {
final count = 0.obs;
void increment() => count.value++;
}
Get.put(Controller());
GetX<Controller>(
builder: (_) => Text('Count: ${_.count.value}'),
);Flows:
class Controller extends FlowController {
final count = 0.obs;
void increment() => count.value++;
}
Flow.put(Controller());
Flx(() => Text('Count: ${logic.state.count.value}'));- Use FlxValue for single values: More efficient than full
Flxrebuilds
// Less efficient
Flx(() => Text('Count: ${logic.state.count.value}'));
// More efficient
FlxValue(logic.state.count, (count) => Text('Count: $count'));- Single-level observation: Flows uses single-level observation for better performance
// Good - direct observation
final count = 0.obs;
// Avoid - nested observation
final data = RxMap({'count': 0.obs}); // Don't do this- Dispose controllers: Always dispose controllers when not needed
@override
void onClose() {
// Cleanup
super.onClose();
}- Use named routes: Named routes are faster than widget navigation
// Faster
Flow.toNamed('/detail', arguments: {'id': 123});
// Slower
Flow.to(DetailPage(id: 123));The example/ directory contains a complete example app demonstrating all features:
- Dependency Injection with
Flow.put/Flow.find - Reactive state management with
Rxtypes andFlxwidgets - Route management with
Flow.to/Flow.toNamed - Logic/State/View separation pattern
- Light/Dark theme switching
- Object passing between routes with editing capabilities
Run the example:
cd example
flutter pub get
flutter runFlows is inspired by the excellent work done on GetX by Jonny Borges. We want to express our gratitude for the innovative approach GetX brought to Flutter development. Flows builds upon these ideas with a focus on performance optimization and API simplicity.
Special thanks to:
- GetX - For pioneering reactive state management and DI in Flutter
- The Flutter team - For building an amazing UI framework
MIT License
Copyright (c) 2026 Flows
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.