SINT
State, Injection, Navigation, Translation — The Four Pillars of High-Fidelity Flutter Infrastructure.
- About SINT
- What's New in 1.3.0
- What's New in 1.2.0
- What's New in 1.1.0
- Installing
- The Four Pillars
- Flutter Web & Deep Links
- Counter App with SINT
- Migration from GetX
- Origin & Philosophy
About SINT
SINT is an architectural evolution of GetX (v5.0.0-rc), built as a focused framework around four pillars only:
| Pillar | Responsibility |
|---|---|
| S — State Management | SintController, SintBuilder, Obx, .obs, Rx types, Workers, SintStatus, SintListener |
| I — Injection | Sint.put, Sint.find, Sint.lazyPut, Sint.putAsync, Bindings, SmartManagement |
| N — Navigation | SintPage, Sint.toNamed, Sint.toInitial, routeParam, pathParam, queryParam, middleware, SintMaterialApp, SintSnackBarStyle, web-safe back() |
| T — Translation | .tr extension, Translations class, locale management, loadTranslations, PathTranslator, translateEndpoints |
Everything outside these four pillars has been removed: no HTTP client, no animations, no string validators, no generic utilities. The result is 37.7% less code than GetX — 12,849 LOC vs 20,615 LOC.
Key principles:
- PERFORMANCE: No Streams or ChangeNotifier overhead. Minimal RAM consumption.
- PRODUCTIVITY: Simple syntax. One import:
import 'package:sint/sint.dart'; - ORGANIZATION: Clean Architecture structure. 5 modules, each mapping to a pillar.
What's New in 1.3.0
Focus: Vanity URLs & Slug Resolution — the missing web piece.
Vanity / Slug URL Resolution
Single-segment vanity URLs now route correctly through unknownRoute:
SintMaterialApp(
initialRoute: '/',
unknownRoute: SintPage(name: '/not-found', page: () => SlugResolverPage()),
sintPages: [
SintPage(name: '/', page: () => HomePage()),
SintPage(name: '/book/:bookId', page: () => BookDetail()),
],
)
// User visits: https://myapp.com/serzenmontoya
// Before 1.3.0: silently redirected to HomePage (GetX bug)
// After 1.3.0: routes to SlugResolverPage with original URL preserved
The problem (inherited from GetX): Any URL starting with / matched the root route /, so unknownRoute never triggered for paths like /serzenmontoya. GetX documents this as: "any string that starts with '/' will correspond to the '/' route". SINT 1.3.0 fixes this.
URL-Preserving unknownRoute
When unknownRoute fires, Sint.currentRoute now returns the original requested URL — not /not-found:
// In your SlugResolverPage
final currentRoute = Sint.currentRoute; // '/serzenmontoya' ✓ (not '/not-found')
final slug = currentRoute.replaceFirst('/', '').split('/').first;
// Resolve the slug against your backend
final match = await MySlugRouter.resolve(slug);
if (match != null) {
Sint.offAllNamed(match.targetRoute);
}
This enables catch-all resolver pages that read the original slug and perform async lookups (Firestore, API, etc.) to identify the content type and navigate to the correct page.
See CHANGELOG.md for the full list of changes.
What's New in 1.2.0
Focus: Flutter Web, Deep Links & i18n URL Routing — without breaking mobile.
RESTful Route Parameters
Spring Boot-inspired parameter extraction that works identically on mobile and web:
// Define routes with path parameters (same as before)
SintPage(name: '/book/:bookId', page: () => BookDetail()),
SintPage(name: '/shop/product/:productId', page: () => ProductPage()),
// Navigate (works on all platforms)
Sint.toNamed('/book/abc123');
Sint.toNamed('/shop/product/42?color=red&size=lg');
// Extract parameters — clean API, no manual parsing
String? bookId = Sint.routeParam; // 'abc123'
String? productId = Sint.pathParam('productId'); // '42'
String? color = Sint.queryParam('color'); // 'red'
String size = Sint.queryParamOrDefault('size', 'm'); // 'lg'
| Method | Equivalent (Spring Boot) | Description |
|---|---|---|
Sint.routeParam |
@PathVariable |
First path parameter value |
Sint.pathParam('id') |
@PathVariable("id") |
Named path parameter |
Sint.queryParam('q') |
@RequestParam |
Query string parameter |
Sint.queryParamOrDefault('sort', 'asc') |
@RequestParam(defaultValue) |
Query with fallback |
All four methods support SintTestMode for unit testing without a running app.
i18n URL Routing (translateEndpoints)
Localized URLs in the browser address bar — zero configuration beyond what you already have:
SintMaterialApp(
translateEndpoints: true, // Enable URL localization
translationsKeys: AppTranslations.keys,
locale: Locale('es'),
sintPages: [
SintPage(name: '/book/:bookId', page: () => BookDetail()),
SintPage(name: '/event/:eventId', page: () => EventDetail()),
],
)
Your existing translations automatically power the URL routing:
// In your translations file — no extra config needed
'es': { 'book': 'libro', 'event': 'evento', ... }
'fr': { 'book': 'livre', 'event': 'evenement', ... }
'de': { 'book': 'buch', 'event': 'veranstaltung', ... }
Result:
| Locale | Browser URL | Internal Route |
|---|---|---|
| EN | /book/abc123 |
/book/abc123 |
| ES | /libro/abc123 |
/book/abc123 |
| FR | /livre/abc123 |
/book/abc123 |
| DE | /buch/abc123 |
/book/abc123 |
How it works:
PathTranslatoris built automatically from your registered routes + translations- Incoming URLs are canonicalized before route matching (
/libro/x→/book/x) - Outgoing URLs are localized for the browser bar (
/book/x→/libro/x) - Diacritics are normalized automatically (
Publicación→publicacion) - On mobile,
translateEndpointshas zero overhead — path translation only activates for web URL parsing
Global Snackbar Theming
Define snackbar appearance once, apply everywhere:
SintMaterialApp(
snackBarStyle: SintSnackBarStyle(
backgroundColor: Colors.grey[900],
colorText: Colors.white,
borderRadius: 12,
margin: EdgeInsets.all(16),
snackPosition: SnackPosition.bottom,
duration: Duration(seconds: 3),
),
)
// All snackbar calls inherit the global style
Sint.snackbar('Title', 'Message');
// Call-site params still override when needed
Sint.snackbar('Error', 'Failed', backgroundColor: Colors.red);
Three-level cascade: call-site > global style > hardcoded defaults.
See CHANGELOG.md for the full list of changes.
What's New in 1.1.0
Reactive Workers
Auto-cancelling reactive listeners on SintController:
class SearchController extends SintController {
final query = ''.obs;
@override
void onInit() {
super.onInit();
debounce(query, (q) => fetchResults(q)); // Wait 400ms after typing stops
once(query, (_) => analytics.track('search')); // Fire once, then auto-cancel
ever(query, (q) => print('Query: $q')); // Every change
interval(query, (q) => save(q)); // Max once per second
}
}
// All subscriptions auto-cancel on onClose(). Zero cleanup code.
SintStatus Pattern Matching
Exhaustive .when() and .maybeWhen() on SintStatus<T>:
final status = SintStatus<User>.loading().obs;
Obx(() => status.value.when(
loading: () => CircularProgressIndicator(),
success: (user) => Text(user.name),
error: (err) => Text('$err'),
empty: () => Text('No data'),
));
// Convenience: status.value.isLoading, .dataOrNull, .errorOrNull
SintListener
React to state without rebuilding (like BLoC's BlocListener):
SintListener<String>(
rx: controller.errorMsg,
listener: (msg) => Sint.snackbar(msg),
child: MyPage(),
)
Async DI
await Sint.putAsync<SharedPreferences>(
() => SharedPreferences.getInstance(),
);
final prefs = Sint.find<SharedPreferences>();
Hard Reset Navigation
Sint.toInitial(); // Full reset
Sint.toInitial(keep: {AuthController}); // Keep auth alive
Lazy Translation Loading
await Sint.loadTranslations(() async {
final json = await rootBundle.loadString('assets/i18n/shop_es.json');
return {'es': Map<String, String>.from(jsonDecode(json))};
});
See CHANGELOG.md for the full list of changes.
Installing
Add SINT to your pubspec.yaml:
dependencies:
sint: ^1.3.0
Import it:
import 'package:sint/sint.dart';
High-Fidelity Performance (Benchmarks)
SINT is built for speed. Every pillar is audited against the Open Neom Standard.
| Pillar | Metric | Result | Context |
|---|---|---|---|
| S (State) | Reactive .obs update |
0.09 us/op | 50,000 updates |
| S (State) | Simple update() |
0.11 us/op | 50,000 updates |
| S (State) | Rx with listener | 6.23 us/op | 30,000 updates stress test |
| I (Injection) | Registry Lookup | 1.34 us/find | Depth 10 dependency resolution |
| N (Navigation) | Middleware Latency | 23 ms | 5-layer middleware chain |
| T (Translation) | Dynamic Interpolation | 2.65 us/op | 10,000 trParams lookups |
Why SINT is faster:
- Pillar S: Avoids Stream overhead by using direct
ListNotifierpropagation. 15-30x faster than BLoC. - Pillar I: O(1) hash lookups in the global registry with lifecycle management.
- Pillar N: Context-less navigation removes heavy widget tree lookups during routing.
The Four Pillars
State Management (S)
Two approaches: Reactive (.obs + Obx) and Simple (SintBuilder).
// Reactive
var count = 0.obs;
Obx(() => Text('${count.value}'));
// Simple
SintBuilder<Controller>(
builder: (_) => Text('${_.counter}'),
)
Workers for reactive side effects:
ever(rx, callback); // Every change
once(rx, callback); // First change only
debounce(rx, callback); // After pause (400ms default)
interval(rx, callback); // Max once per duration (1s default)
SintStatus for async state:
status.value.when(
loading: () => spinner,
success: (data) => content(data),
error: (err) => errorView(err),
empty: () => emptyView,
);
Injection (I)
Dependency injection without context:
Sint.put(AuthController());
Sint.lazyPut(() => ApiService());
await Sint.putAsync(() => SharedPreferences.getInstance());
final controller = Sint.find<AuthController>();
Navigation (N)
Route management without context — optimized for web deep links and mobile alike:
SintMaterialApp(
initialRoute: '/',
translateEndpoints: true, // i18n URLs (web)
snackBarStyle: SintSnackBarStyle(...), // Global theming
sintPages: [
SintPage(name: '/', page: () => Home()),
SintPage(name: '/book/:bookId', page: () => BookDetail()),
SintPage(name: '/search', page: () => Search()),
],
)
// Navigation
Sint.toNamed('/book/abc123?ref=home');
Sint.back(); // Web-safe
Sint.toInitial(); // Hard reset to home
Sint.toInitial(keep: {AuthController}); // Keep specific controllers
// RESTful parameter extraction
String? id = Sint.routeParam; // 'abc123'
String? id = Sint.pathParam('bookId'); // 'abc123'
String? ref = Sint.queryParam('ref'); // 'home'
String sort = Sint.queryParamOrDefault('sort', 'a'); // 'a' (default)
// Snackbar with global style
Sint.snackbar('Title', 'Message');
Translation (T)
Internationalization with .tr — now powers URL routing too:
Text('hello'.tr);
Text('welcome'.trParams({'name': 'Serzen'}));
Sint.updateLocale(Locale('es', 'ES'));
// Lazy loading per module
await Sint.loadTranslations(() async {
final json = await rootBundle.loadString('assets/i18n/shop.json');
return {'es': Map<String, String>.from(jsonDecode(json))};
});
// URL path translation (automatic when translateEndpoints: true)
// Your translation keys double as URL segment mappings:
// 'book' → 'libro' (ES), 'livre' (FR), 'buch' (DE)
//
// PathTranslator handles:
// canonicalizePath('/libro/abc') → '/book/abc' (incoming)
// localizePath('/book/abc', 'es') → '/libro/abc' (outgoing)
Flutter Web & Deep Links
SINT is designed with a web-first, mobile-safe philosophy. Every feature works identically across platforms, but web gets extra optimizations:
| Feature | Web Behavior | Mobile Behavior |
|---|---|---|
Sint.back() |
No-op if no internal history (browser arrows handle it) | Standard Navigator.pop() |
Sint.routeParam |
Extracted from browser URL path | Extracted from route arguments |
Sint.queryParam() |
Extracted from URL query string ?key=value |
Extracted from route arguments |
translateEndpoints |
Localizes browser URL bar + canonicalizes incoming URLs | No overhead — flag is ignored |
| Vanity / slug URLs | unknownRoute fires correctly for /slug paths; original URL preserved |
Same behavior via deep links |
Sint.showBackButton |
false (browser has native arrows) |
true |
| Default transition | Transition.fade (GPU-light for web canvas) |
Platform default (Cupertino/Material) |
| Scroll behavior | Drag enabled for touch, mouse, and trackpad | Platform default |
SintSnackBarStyle |
Same styling across web and mobile | Same styling across web and mobile |
Deep Link Example (Web + Mobile)
// 1. Define routes with parameters
SintMaterialApp(
initialRoute: '/',
translateEndpoints: true,
translationsKeys: AppTranslations.keys,
locale: Locale('es'),
sintPages: [
SintPage(name: '/', page: () => HomePage()),
SintPage(name: '/book/:bookId', page: () => BookDetail()),
SintPage(name: '/profile/:userId', page: () => ProfilePage()),
],
)
// 2. In your controller — same code works everywhere
class BookDetailController extends SintController {
late final String bookId;
@override
void onInit() {
super.onInit();
// Works from: browser URL, deep link, or Sint.toNamed()
bookId = Sint.routeParam ?? '';
loadBook(bookId);
}
}
On web: User visits https://myapp.com/libro/abc123 →
SINT canonicalizes to /book/abc123 → routes to BookDetail →
Sint.routeParam returns 'abc123' → browser shows /libro/abc123.
On mobile: Sint.toNamed('/book/abc123') →
routes to BookDetail → Sint.routeParam returns 'abc123'.
Same controller. Same routes. Same parameters. Zero platform checks.
Counter App with SINT
void main() => runApp(SintMaterialApp(
initialRoute: '/',
sintPages: [
SintPage(name: '/', page: () => Home()),
SintPage(name: '/other', page: () => Other()),
],
));
class Controller extends SintController {
var count = 0.obs;
increment() => count++;
}
class Home extends StatelessWidget {
@override
Widget build(context) {
final c = Sint.put(Controller());
return Scaffold(
appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
body: Center(
child: ElevatedButton(
child: Text("Go to Other"),
onPressed: () => Sint.toNamed('/other'),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: c.increment,
),
);
}
}
class Other extends StatelessWidget {
final Controller c = Sint.find();
@override
Widget build(context) {
return Scaffold(body: Center(child: Text("${c.count}")));
}
}
Migration from GetX
- Replace
get:withsint:inpubspec.yaml - Replace
import 'package:get/get.dart'withimport 'package:sint/sint.dart' - Your existing
Get.calls work — gradually replace withSint.to remove deprecation warnings GetMaterialApp→SintMaterialAppGetPage→SintPage
Origin & Philosophy
SINT is a hard fork of GetX v5.0.0-rc. After 8 years of accumulated code, GetX's repository became inactive and carried significant unused weight. SINT strips away everything that does not serve the four pillars, resulting in a clean, maintainable foundation built with Clean Architecture principles.
GetX: "Do everything." SINT: "Do the right things."
S + I + N + T — State, Injection, Navigation, Translation. Nothing more, nothing less.
License
SINT is released under the MIT License.
Part of the Open Neom ecosystem.
Libraries
- core/sint_core
- core/src/domain/enums/smart_management
- core/src/domain/errors/bind_error
- core/src/domain/errors/obx_error
- core/src/domain/extensions/sint_reset
- core/src/domain/interfaces/sint_interface
- core/src/domain/models/rx_bool
- core/src/domain/models/rx_custom
- core/src/domain/models/rx_impl
- core/src/domain/models/rx_interface
- core/src/domain/models/rx_list
- core/src/domain/models/rx_map
- core/src/domain/models/rx_num
- core/src/domain/models/rx_set
- core/src/domain/models/rx_string
- core/src/domain/typedefs/core_typedefs
- core/src/domain/typedefs/legacy_typedefs
- core/src/sint_engine
- core/src/sint_main
- core/src/sint_queue
- core/src/utils/log
- core/src/utils/testing/wrapper
- core/src/utils/testing/wrapper_named
- core/src/utils/testing/wrapper_translations
- injection/sint_injection
- injection/src/bind
- injection/src/domain/extensions/injection_extension
- injection/src/domain/interfaces/binding
- injection/src/domain/interfaces/bindings_interface
- injection/src/domain/models/binds
- injection/src/domain/models/instance_info
- injection/src/domain/typedefs/injection_typedefs
- injection/src/lifecycle
- injection/src/ui/bind_element
- injection/src/ui/binder
- navigation/src/domain/enums/pop_mode
- navigation/src/domain/enums/prevent_duplicate_handling_mode
- navigation/src/domain/enums/row_style
- navigation/src/domain/enums/snack_hover_state
- navigation/src/domain/enums/snackbar_position
- navigation/src/domain/enums/snackbar_status
- navigation/src/domain/enums/snackbar_style
- navigation/src/domain/enums/transition
- navigation/src/domain/extensions/bottomsheet_extension
- navigation/src/domain/extensions/context_extensions
- navigation/src/domain/extensions/dialog_extension
- navigation/src/domain/extensions/event_loop_extension
- navigation/src/domain/extensions/first_where_extension
- navigation/src/domain/extensions/overlay_extension
- navigation/src/domain/extensions/page_arg_extension
- navigation/src/domain/extensions/snackbar_extension
- navigation/src/domain/interfaces/custom_transition
- navigation/src/domain/interfaces/sint_middleware
- navigation/src/domain/mixins/sint_transition_mixin
- navigation/src/domain/models/config_data
- navigation/src/domain/models/path_decoded
- navigation/src/domain/models/route_data
- navigation/src/domain/models/route_node
- navigation/src/domain/models/routing
- navigation/src/domain/models/sint_snackbar_style
- navigation/src/router/circular_reveal_clipper
- navigation/src/router/index
- navigation/src/router/middleware_runner
- navigation/src/router/page_redirect
- navigation/src/router/page_settings
- navigation/src/router/route_decoder
- navigation/src/router/route_match_result
- navigation/src/router/route_matcher
- navigation/src/router/route_parser
- navigation/src/router/route_tree
- navigation/src/router/route_tree_result
- navigation/src/router/router_report_manager
- navigation/src/router/sint_delegate
- navigation/src/router/sint_information_parser
- navigation/src/router/sint_page
- navigation/src/router/sint_page_route
- navigation/src/router/sint_test_mode
- navigation/src/router/url_strategy/impl/io_url
- navigation/src/router/url_strategy/impl/stub_url
- navigation/src/router/url_strategy/impl/web_url
- navigation/src/router/url_strategy/url_strategy
- navigation/src/ui/bottomsheet/modal_bottomsheet_layout
- navigation/src/ui/bottomsheet/modal_bottomsheet_route
- navigation/src/ui/dialog/dialog_route
- navigation/src/ui/sint_cupertino_app
- navigation/src/ui/sint_material_app
- navigation/src/ui/sint_root
- navigation/src/ui/snackbar/snackbar
- navigation/src/ui/snackbar/snackbar_controller
- navigation/src/ui/snackbar/snackbar_queue
- navigation/src/ui/transitions/circular_reveal_transition
- navigation/src/ui/transitions/fade_in_transition
- navigation/src/ui/transitions/left_to_right_fade_transition
- navigation/src/ui/transitions/no_transition
- navigation/src/ui/transitions/right_to_left_fade_transition
- navigation/src/ui/transitions/size_transitions
- navigation/src/ui/transitions/slide_down_transition
- navigation/src/ui/transitions/slide_left_transition
- navigation/src/ui/transitions/slide_right_transition
- navigation/src/ui/transitions/slide_top_transition
- navigation/src/ui/transitions/zoom_in_transition
- navigation/src/ui/widgets/directionality_drag_gesture_recognizer
- navigation/src/ui/widgets/sint_app_bar
- navigation/src/ui/widgets/sint_back_gesture_controller
- navigation/src/ui/widgets/sint_back_gesture_detector
- sint
- SINT Framework 1.0.0
- state_manager/sint_state_manager
- state_manager/src/domain/mixins/equality_mixin
- state_manager/src/domain/mixins/rx_ticket_provider_mixin
- state_manager/src/domain/notify_data
- state_manager/src/domain/sint_status
- state_manager/src/domain/typedefs/state_typedefs
- state_manager/src/engine/get_builder
- state_manager/src/engine/list_notifier
- state_manager/src/engine/notifier
- state_manager/src/engine/sint_controller
- state_manager/src/sint_listenable
- state_manager/src/ui/obx_reacive_element
- state_manager/src/ui/obx_widget
- state_manager/src/ui/sint_listener
- translation/sint_translation
- translation/src/domain/extensions/locale_extension
- translation/src/domain/extensions/trans_extension
- translation/src/domain/interfaces/translations
- translation/src/domain/models/intl_host
- translation/src/domain/models/path_translator
- translation/src/utils/translations_constants