jetx 0.1.0-alpha copy "jetx: ^0.1.0-alpha" to clipboard
jetx: ^0.1.0-alpha copied to clipboard

A modern Flutter framework for state management, dependency injection, and route management. An actively maintained fork of GetX.

JetX #

pub package popularity likes pub points style: effective dart

A modern, powerful, and lightweight Flutter framework for state management, dependency injection, and route management.

jetx is an actively maintained and enhanced fork of GetX, bringing improved performance, better organization, and modern Flutter practices.


Table of Contents #


What is JetX? #

jetx combines high-performance state management, intelligent dependency injection, and intuitive route management into a single, cohesive package for Flutter.

Three Core Principles:

  • πŸš€ Performance - No Streams or ChangeNotifier overhead. Minimal resource consumption.
  • ⚑ Productivity - Simple, elegant syntax that saves hours of development time.
  • πŸ“¦ Organization - Complete decoupling of UI, business logic, and navigation. No context needed.

jetx is modular by design. Use only what you needβ€”if you only use state management, only that code is compiled. Each feature is independently usable without bloating your app.


πŸ†• What's New in JetX #

JetX introduces powerful new reactive programming features that significantly enhance developer productivity and code quality. These features are not available in the original GetX.

✨ Computed Values - Automatic Derived State #

Create reactive values that automatically recompute when dependencies change - no manual updates needed!

class CartController extends JetxController {
  final items = <Item>[].obs;
  final taxRate = 0.08.obs;
  
  // Automatically recomputes when items change!
  late final subtotal = computed(
    () => items.fold(0.0, (sum, item) => sum + item.price),
    watch: [items],
  );
  
  // Chain computed values
  late final tax = computed(
    () => subtotal.value * taxRate.value,
    watch: [subtotal, taxRate],
  );
  
  late final total = computed(
    () => subtotal.value + tax.value,
    watch: [subtotal, tax],
  );
}

// UI updates automatically when items or taxRate changes!
Obx(() => Text('Total: \$${controller.total.value}'))

✨ Reactive Operators - Functional Transformations #

Transform, filter, and combine reactive values with ease.

// Transform
final temperature = 0.obs;
final fahrenheit = temperature.map((c) => c * 9/5 + 32);

// Filter
final input = ''.obs;
final validInput = input.where((text) => text.length >= 3);

// Combine
final firstName = 'John'.obs;
final lastName = 'Doe'.obs;
final fullName = Rx.combine2(
  firstName, lastName,
  (a, b) => '$a $b',
);

// Accumulate
final events = 0.obs;
final totalEvents = events.scan<int>(
  0,
  (total, event, i) => total + event,
);

✨ Stream Integration - Seamless Stream Binding #

Bridge the gap between reactive programming and stream-based APIs.

// Auto-managed
final status = Rx.fromStream(statusStream, initial: Status.idle);

// Manual management in controllers
class ChatController extends JetxController {
  final messages = <Message>[].obs;
  late StreamSubscription _sub;
  
  @override
  void onInit() {
    super.onInit();
    _sub = messages.listenToStream(chatService.messagesStream);
  }
  
  @override
  void onClose() {
    _sub.cancel();
    super.onClose();
  }
}

πŸš€ Why These Features Matter #

  • πŸš€ Zero Boilerplate - No manual state synchronization
  • ⚑ Performance - Only recomputes when dependencies change
  • πŸ”— Declarative - Clean, functional programming patterns
  • πŸ›‘οΈ Type Safe - Full type safety and null safety support
  • πŸ§ͺ Testable - Easy to test with predictable behavior

πŸ“š Learn More #

  • Complete What's New Guide β†’ - Detailed overview of all new features
  • State Management Guide β†’ - Complete guide including new reactive features
  • Quick Reference β†’ - Fast lookup for all JetX features

Quick Start #

Installation #

Add JetX to your pubspec.yaml:

dependencies:
  jetx: ^1.0.0

Import JetX in your files:

import 'package:jetx/jetx.dart';

Counter App Example #

The power of JetX in 26 lines of code - A complete counter app with state management, navigation, and shared state between screens.

Step 1: Wrap your MaterialApp with JetMaterialApp

void main() => runApp(JetMaterialApp(home: Home()));

Note: JetMaterialApp is a pre-configured MaterialApp. It's only required if you use route management (Jet.to(), Jet.back(), etc.). For state management alone, it's optional.

Step 2: Create your controller with observable variables

class Controller extends JetxController {
  var count = 0.obs;
  increment() => count++;
}

Step 3: Build your view with reactive updates

class Home extends StatelessWidget {
  @override
  Widget build(context) {
    // Register controller
    final Controller c = Jet.put(Controller());

    return Scaffold(
      // Auto-updates when count changes
      appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
      
      body: Center(
        child: ElevatedButton(
          child: Text("Go to Other"),
          onPressed: () => Jet.to(Other()), // Navigate without context!
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: c.increment,
      ),
    );
  }
}

class Other extends StatelessWidget {
  // Find the existing controller
  final Controller c = Jet.find();

  @override
  Widget build(context) {
    // Access the same count value
    return Scaffold(
      body: Center(child: Obx(() => Text("${c.count}"))),
    );
  }
}

That's it! State management, navigation, and dependency injection working together seamlessly.

Key Concepts at a Glance #

Feature How to Use
Make variable reactive var name = 'John'.obs;
Update UI automatically Obx(() => Text(controller.name))
Navigate to screen Jet.to(NextScreen());
Navigate with name Jet.toNamed('/details');
Go back Jet.back();
Register dependency Jet.put(Controller());
Find dependency Jet.find<Controller>();
Show snackbar Jet.snackbar('Title', 'Message');
Show dialog Jet.dialog(AlertDialog(...));

Core Features #

State Management #

TL;DR: Add .obs to make variables reactive, wrap widgets in Obx() to auto-update. No code generators, no boilerplate.

jetx offers powerful state management with new reactive features not available in GetX:

  • Simple State Manager (JetBuilder) - Lightweight, manual control
  • Reactive State Manager (Obx) - Automatic updates when observables change
  • πŸ†• Advanced Reactive Features - Computed values, operators, stream integration

Reactive Example

// In your controller
var name = 'John'.obs;

// In your UI
Obx(() => Text(name.value));

When name changes, the UI updates automatically. No setState(), no StreamBuilder, no complications.

πŸ†• Advanced Reactive Features

Computed Values - Automatic derived state:

late final total = computed(
  () => items.fold(0.0, (sum, item) => sum + item.price),
  watch: [items],
);

Reactive Operators - Transform, filter, combine:

final validInput = input.where((text) => text.length >= 3);
final fullName = Rx.combine2(firstName, lastName, (a, b) => '$a $b');

Stream Integration - Seamless stream binding:

final status = Rx.fromStream(statusStream, initial: Status.idle);

Quick Reference

// Make it observable
var count = 0.obs;
var name = 'John'.obs;
var isLogged = false.obs;
var list = [].obs;

// Update values
count.value = 10;
count++;                    // Works directly!
name.value = 'Jane';
isLogged.toggle();         // Toggles true/false
list.add('item');          // List updates are reactive too

πŸ“š Read the complete State Management guide β†’


Route Management #

TL;DR: Navigate without context. Use Jet.to(), Jet.back(), and named routes. Simple and powerful.

Basic Navigation

// Navigate to next screen
Jet.to(NextScreen());

// Navigate with name
Jet.toNamed('/profile');

// Go back
Jet.back();

// Go to next screen and remove previous
Jet.off(NextScreen());

// Go to next screen and clear all previous routes
Jet.offAll(LoginScreen());

Named Routes

JetMaterialApp(
  getPages: [
    JetPage(name: '/', page: () => HomeScreen()),
    JetPage(name: '/profile', page: () => ProfileScreen()),
    JetPage(name: '/settings', page: () => SettingsScreen()),
  ],
)

Advanced Navigation

// Navigate with arguments
Jet.toNamed('/user', arguments: {'id': 123});

// Receive arguments
final args = Jet.arguments;

// Navigate with result
var result = await Jet.to(SelectionScreen());

// Conditional navigation until
Jet.offUntil(HomeScreen(), (route) => route.isFirst);

πŸ“š Read the complete Route Management guide β†’


Dependency Injection #

TL;DR: Register with Jet.put(), retrieve with Jet.find(). Automatic memory management. No Provider trees needed.

Basic Usage

// Register a dependency
Jet.put(ApiController());

// Use it anywhere in your app
final controller = Jet.find<ApiController>();
controller.fetchData();

Lifecycle Management

// Lazy instantiation (created when first used)
Jet.lazyPut(() => HeavyController());

// Async instantiation
await Jet.putAsync(() => DatabaseService().init());

// Keep in memory permanently
Jet.put(CacheService(), permanent: true);

// Delete when not needed
Jet.delete<ApiController>();

Bindings

Group related dependencies for cleaner code:

class HomeBinding extends Bindings {
  @override
  void dependencies() {
    Jet.lazyPut(() => HomeController());
    Jet.lazyPut(() => HomeService());
  }
}

// Use with routes
JetPage(
  name: '/home',
  page: () => HomeScreen(),
  binding: HomeBinding(),
)

πŸ“š Read the complete Dependency Management guide β†’


UI Features #

Theming #

Switch themes dynamically without boilerplate.

// Change theme
Jet.changeTheme(ThemeData.dark());

// Toggle dark mode
Jet.changeTheme(Jet.isDarkMode ? ThemeData.light() : ThemeData.dark());

// Check current theme
if (Jet.isDarkMode) {
  // Dark mode is active
}

Dialogs & Snackbars #

Show dialogs and snackbars without context.

// Simple dialog
Jet.defaultDialog(
  title: 'Confirmation',
  middleText: 'Are you sure?',
  onConfirm: () => Jet.back(),
);

// Snackbar
Jet.snackbar('Success', 'Operation completed!');

// Bottom sheet
Jet.bottomSheet(Container(child: Text('Content')));

Context Extensions #

Powerful context extensions for responsive design.

// Screen dimensions
Container(
  width: context.width * 0.8,
  height: context.height * 0.5,
)

// Responsive values
Text(
  'Responsive Text',
  style: TextStyle(
    fontSize: context.responsiveValue<double>(
      mobile: 16,
      tablet: 20,
      desktop: 24,
    ),
  ),
)

πŸ“š Complete UI Features Guide β†’


Additional Features #

Internationalization #

Simple key-value translations without complexity.

// Define translations
class Messages extends Translations {
  @override
  Map<String, Map<String, String>> get keys => {
    'en_US': {'hello': 'Hello World'},
    'es_ES': {'hello': 'Hola Mundo'},
  };
}

// Configure app
JetMaterialApp(
  translations: Messages(),
  locale: Locale('en', 'US'),
  fallbackLocale: Locale('en', 'US'),
)

// Use in UI
Text('hello'.tr);

// Change locale
Jet.updateLocale(Locale('es', 'ES'));

Advanced Translation Features

// With parameters
'welcome'.trParams({'name': 'John'});

// Plural support
'item'.trPlural('items', count);

πŸ“š Complete Internationalization Guide β†’


JetConnect - HTTP & WebSockets #

Easy communication with your backend.

HTTP Requests

class UserProvider extends JetConnect {
  Future<Response> getUser(int id) => get('https://api.example.com/users/$id');
  
  Future<Response> createUser(Map data) => post('https://api.example.com/users', body: data);
  
  Future<Response> updateUser(int id, Map data) => put('https://api.example.com/users/$id', body: data);
}

Custom Configuration

class ApiProvider extends JetConnect {
  @override
  void onInit() {
    httpClient.baseUrl = 'https://api.example.com';
    httpClient.defaultDecoder = User.fromJson;
    
    // Add auth header to all requests
    httpClient.addRequestModifier((request) {
      request.headers['Authorization'] = 'Bearer $token';
      return request;
    });
    
    // Transform responses
    httpClient.addResponseModifier((request, response) {
      // Process response
      return response;
    });
  }
}

WebSocket Support

class ChatProvider extends JetConnect {
  JetSocket chatRoom() {
    return socket('wss://api.example.com/chat');
  }
}

// Usage
final socket = provider.chatRoom();
socket.onMessage((data) => print('Message: $data'));
socket.send('Hello!');

Middleware #

Control route access and lifecycle with middleware.

class AuthMiddleware extends JetMiddleware {
  @override
  int? get priority => 1;

  @override
  RouteSettings? redirect(String? route) {
    final authService = Jet.find<AuthService>();
    return authService.isAuthenticated ? null : RouteSettings(name: '/login');
  }

  @override
  JetPage? onPageCalled(JetPage? page) {
    // Modify page before creation
    return page;
  }
}

// Use with routes
JetPage(
  name: '/dashboard',
  page: () => DashboardScreen(),
  middlewares: [AuthMiddleware()],
)

🎯 Modern Reactive Features #

jetx includes cutting-edge reactive programming features inspired by modern frameworks like Riverpod, while maintaining JetX's signature simplicity.

Computed Values - Auto-Updating Derived State #

Create reactive values that automatically recompute when dependencies change. No manual calculations or state updates needed!

class CartController extends JetxController {
  final items = <Item>[].obs;
  final taxRate = 0.08.obs;
  
  // Automatically recalculates when items change!
  late final subtotal = computed(
    () => items.fold(0.0, (sum, i) => sum + i.price),
    watch: [items],
  );
  
  // Chain computed values
  late final tax = computed(
    () => subtotal.value * taxRate.value,
    watch: [subtotal, taxRate],
  );
  
  late final total = computed(
    () => subtotal.value + tax.value,
    watch: [subtotal, tax],
  );
  
  // Add an item - totals update automatically!
  void addItem(Item item) => items.add(item);
}

// UI updates automatically - no manual calculations!
Obx(() => Text('Total: \$${controller.total.value}'))

Reactive Operators - Functional Transformations #

Transform, filter, and combine reactive values with ease.

// Transform
final temperature = 0.obs;
final fahrenheit = temperature.map((c) => c * 9/5 + 32);

// Filter
final input = ''.obs;
final validInput = input.where((text) => text.length >= 3);

// Combine
final firstName = 'John'.obs;
final lastName = 'Doe'.obs;
final fullName = Rx.combine2(
  firstName, lastName,
  (a, b) => '$a $b',
);

// Accumulate
final events = 0.obs;
final totalEvents = events.scan<int>(
  0,
  (total, event, i) => total + event,
);

Stream Integration #

Seamlessly bind Dart Streams to reactive values.

class ChatController extends JetxController {
  final messages = <Message>[].obs;
  late StreamSubscription _sub;
  
  @override
  void onInit() {
    super.onInit();
    // Auto-sync with stream
    _sub = messages.listenToStream(chatService.messagesStream);
  }
  
  @override
  void onClose() {
    _sub.cancel();
    super.onClose();
  }
}

// Or use the helper
final status = Rx.fromStream(
  statusStream,
  initial: Status.idle,
);

πŸ“š Learn More: Check out the complete Advanced Features Guide with real-world examples!


Advanced Topics #

Local State Widgets #

Manage ephemeral state without creating full controllers.

ValueBuilder

Simple state management with callbacks:

ValueBuilder<bool>(
  initialValue: false,
  builder: (value, updateFn) => Switch(
    value: value,
    onChanged: updateFn,
  ),
  onUpdate: (value) => print("Value: $value"),
)

ObxValue

Reactive version using observables:

ObxValue(
  (data) => Switch(
    value: data.value,
    onChanged: (val) => data.value = val,
  ),
  false.obs,
)

JetView, JetWidget & JetxService #

JetView

A StatelessWidget with automatic controller access:

class HomeView extends JetView<HomeController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(controller.title)),
      body: Obx(() => Text('Count: ${controller.count}')),
    );
  }
}

No need to call Jet.find() - the controller getter is automatically available.

JetWidget

Caches controller instances - useful with Jet.create():

class ProductTile extends JetWidget {
  @override
  Widget build(BuildContext context) {
    final controller = Jet.find<ProductController>();
    return ListTile(title: Text(controller.name));
  }
}

JetxService

Services that persist for the entire app lifetime:

class CacheService extends JetxService {
  @override
  Future<void> onInit() async {
    super.onInit();
    await loadCache();
  }
}

// Initialize before app runs
Future<void> main() async {
  await initServices();
  runApp(MyApp());
}

void initServices() async {
  await Jet.putAsync(() => CacheService());
  await Jet.putAsync(() => DatabaseService());
}

JetxService cannot be removed from memory except with Jet.reset() - perfect for app-wide services.


StateMixin #

Handle UI states (loading, success, error, empty) elegantly.

class UserController extends JetxController with StateMixin<User> {
  @override
  void onInit() {
    super.onInit();
    fetchUser();
  }

  void fetchUser() async {
    change(null, status: RxStatus.loading());
    
    try {
      final user = await api.getUser();
      change(user, status: RxStatus.success());
    } catch (e) {
      change(null, status: RxStatus.error(e.toString()));
    }
  }
}

// In your view
class UserView extends JetView<UserController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: controller.obx(
        (user) => Text('Hello ${user.name}'),
        onLoading: CircularProgressIndicator(),
        onError: (error) => Text('Error: $error'),
        onEmpty: Text('No user found'),
      ),
    );
  }
}

Reactive Programming Deep Dive #

Observable Types

// Basic types
final name = 'John'.obs;           // RxString
final age = 25.obs;                // RxInt
final price = 9.99.obs;            // RxDouble
final isActive = true.obs;         // RxBool

// Collections
final items = <String>[].obs;      // RxList
final user = <String, dynamic>{}.obs; // RxMap
final tags = <String>{}.obs;       // RxSet

// Custom objects
final user = User().obs;           // Rx<User>

Reactive Operators

final count = 0.obs;

// Direct operations
count++;                           // Increments and notifies
count += 5;
count.value = 10;

// Boolean operations
final flag = false.obs;
flag.toggle();                     // Switches true/false

// List operations (all are reactive)
final list = [].obs;
list.add('item');
list.addAll(['a', 'b']);
list.remove('item');
list.clear();

// Workers - React to changes
ever(count, (value) => print('Count changed to $value'));
once(count, (value) => print('Executed once'));
debounce(count, (value) => print('Debounced'), time: Duration(seconds: 1));
interval(count, (value) => print('Every second'), time: Duration(seconds: 1));

Custom Reactive Models

class User {
  String name;
  int age;
  User({required this.name, required this.age});
}

final user = User(name: 'John', age: 25).obs;

// Update and notify
user.update((val) {
  val!.name = 'Jane';
  val.age = 26;
});

// Or manually refresh
user.value.name = 'Bob';
user.refresh();

Testing #

jetx controllers are easy to test with full lifecycle support.

class CounterController extends JetxController {
  var count = 0.obs;
  
  @override
  void onInit() {
    super.onInit();
    count.value = 5;
  }
  
  void increment() => count++;
  
  @override
  void onClose() {
    count.value = 0;
    super.onClose();
  }
}

void main() {
  test('Counter lifecycle test', () {
    // Without lifecycle
    final controller = CounterController();
    expect(controller.count.value, 0);
    
    // With lifecycle
    Jet.put(controller); // Calls onInit
    expect(controller.count.value, 5);
    
    controller.increment();
    expect(controller.count.value, 6);
    
    Jet.delete<CounterController>(); // Calls onClose
    expect(controller.count.value, 0);
  });
}

Testing Tips

Mock Services:

class ApiServiceMock extends JetxService with Mock implements ApiService {}

// Use in tests
Jet.put<ApiService>(ApiServiceMock());

Test Mode:

void main() {
  Jet.testMode = true; // Enable test mode for navigation
  
  // Your tests...
}

Reset Between Tests:

tearDown(() {
  Jet.reset(); // Clears all dependencies
});

Migration Guide #

From GetX to JetX #

jetx maintains API compatibility with GetX while adding improvements. Migration is straightforward:

1. Update Dependencies

dependencies:
  JetX: ^1.0.0  # Replace 'get'

2. Update Imports

// Old
import 'package:get/get.dart';

// New
import 'package:jetx/jetx.dart';

3. Update API Calls

GetX JetX
Get.to() Jet.to()
Get.put() Jet.put()
Get.find() Jet.find()
GetMaterialApp JetMaterialApp
GetxController JetxController
GetBuilder JetBuilder
GetView JetView
GetWidget JetWidget
GetxService JetxService
GetPage JetPage
GetConnect JetConnect

Most functionality remains the same, just with the new naming convention.

Breaking Changes from GetX 2.x #

If migrating from old GetX versions:

Rx Types Updated:

Old New
StringX RxString
IntX RxInt
MapX RxMap
ListX RxList
NumX RxNum
DoubleX RxDouble

Named Routes Structure:

// Old (GetX 2.x)
JetMaterialApp(
  namedRoutes: {
    '/': JetRoute(page: Home()),
  }
)

// New (jetx)
JetMaterialApp(
  getPages: [
    JetPage(name: '/', page: () => Home()),
  ]
)

Why JetX? #

Compared to Other Solutions #

Feature JetX Provider BLoC Riverpod
State Management βœ… βœ… βœ… βœ…
Route Management βœ… ❌ ❌ ❌
Dependency Injection βœ… βœ… ⚠️ βœ…
No Context Required βœ… ❌ ❌ βœ…
No Code Generation βœ… βœ… ❌ ❌
Minimal Boilerplate βœ… ⚠️ ❌ ⚠️
Learning Curve Easy Easy Steep Medium

Key Advantages #

1. All-in-One Solution

  • State management, routing, and dependency injection in one cohesive package
  • No need to mix and match multiple packages
  • Consistent API across all features

2. Performance First

  • No Streams or ChangeNotifier overhead
  • Smart memory management - unused dependencies are automatically removed
  • Lazy loading by default
  • Minimal rebuilds with surgical precision

3. Developer Productivity

  • Simple, intuitive syntax
  • No context required for navigation or dependency access
  • No code generators or build runners
  • Less boilerplate = faster development

4. Production Ready

  • Battle-tested by thousands of apps (GetX heritage)
  • Actively maintained with regular updates
  • Comprehensive documentation and examples
  • Strong community support

5. Flexibility

  • Use only what you need - modular by design
  • Mix with other packages if desired
  • Works with any architecture (MVC, MVVM, Clean, etc.)

Community & Contributing #

Get Help #

Contributing #

We welcome contributions! Here's how you can help:

  • πŸ› Report bugs - Open an issue with details
  • πŸ’‘ Suggest features - Share your ideas for improvements
  • πŸ“– Improve docs - Help make documentation clearer
  • πŸ”§ Submit PRs - Fix bugs or add features
  • ⭐ Star the repo - Show your support

Development Setup

# Clone the repository
git clone https://github.com/alamre/jetx.git
cd JetX

# Install dependencies
flutter pub get

# Run tests
flutter test

# Run example
cd example
flutter run

Code of Conduct #

Be respectful, constructive, and welcoming. We're here to build great software together.


License #

jetx is released under the MIT License.


Acknowledgments #

jetx is built upon the foundation of GetX, created by Jonatas Borges. We're grateful to the original GetX community and all contributors who made this framework possible. JetX continues that legacy with enhanced features and active maintenance.


⬆ Back to Top

Made with ❀️ by the JetX community

12
likes
0
points
54
downloads

Publisher

verified publishershrimps.ly

Weekly Downloads

A modern Flutter framework for state management, dependency injection, and route management. An actively maintained fork of GetX.

Repository (GitHub)
View/report issues

Documentation

Documentation

License

unknown (license)

Dependencies

flutter

More

Packages that depend on jetx