swift_flutter 2.3.3
swift_flutter: ^2.3.3 copied to clipboard
Reactive state management for Flutter with automatic dependency tracking, Swift-like extensions, and SwiftUI-like animations.
swift_flutter #
A reactive state management library for Flutter with automatic dependency tracking. Inspired by MobX and Vue's reactivity system, but built specifically for Flutter with Swift-like extensions and two flexible patterns.
๐ Learn more at swiftflutter.com
โจ Features #
โ
Two Flexible Patterns - Direct state management OR Controller pattern with enforced separation
โ
Reactive State (SwiftValue) - Automatic dependency tracking with swift()
โ
Swift Widget - Auto-rebuild when dependencies change
โ
Computed (Derived State) - Automatically computed values with nested dependency support
โ
SwiftFuture / Async State - Loading/error/success states with automatic retry and error recovery
โ
SwiftField / Form Validation - Field validation with built-in validators
โ
SwiftPersisted / Persistence - Automatic save/load with migration support
โ
SwiftController - Enforced separation of concerns (views read-only, controllers modify)
โ
Swift-like Extensions - Toggle, add, sub, mul, div, and 80+ convenient methods
โ
Middleware / Interceptors - Action interception and logging
โ
Batch Update Transactions - Prevent unnecessary rebuilds
โ
Debug Logger - Configurable logging with history
โ
SwiftTween / Animation Tween - Reactive animation values with AnimationController
โ
SwiftUI-like Declarative Animations - Zero boilerplate animations (no controllers, no mixins!)
โ
Lifecycle Controller - Widget lifecycle management
โ
Global Store / Dependency Injection - Service registration and state management
โ
Redux-like Reducers - Predictable state updates with action/reducer pattern
โ
State Normalization - Efficient collection management
โ
Pagination - Built-in pagination support
โ
Error Boundaries - Error handling with UI components
โ
Performance Monitoring - Built-in performance tracking
โ
Circular Dependency Detection - Automatic detection of circular dependencies
โ
Memoization - Performance optimization for computed values
โ
Enhanced Testing Utilities - Comprehensive test helpers
โ
DevTools Integration - Full Flutter DevTools support with zero overhead
โ
Debug Tool - Network request/response interceptor with WebSocket support
โ
Network Interceptor - Automatic HTTP request capture for http and dio packages
โ
WebSocket Interceptor - Real-time WebSocket connection and event tracking
โ
Log Interceptor - Capture and view all print statements and logs
๐ฆ Installation #
Add this to your package's pubspec.yaml file:
dependencies:
swift_flutter: ^2.3.2
Then run:
flutter pub get
๐ Quick Start #
Pattern 1: Direct State Management (Simple & Flexible) #
Perfect for view-local state, simple UI, and quick prototypes.
import 'package:swift_flutter/swift_flutter.dart';
class MyWidget extends StatefulWidget {
@override
Widget build(BuildContext context) {
// โ
Create reactive state directly in views
final counter = swift(0);
final name = swift('Hello');
final isExpanded = swift(false);
return Swift(
builder: (context) => Column(
children: [
Text('Count: ${counter.value}'),
Text('Name: ${name.value}'),
ElevatedButton(
onPressed: () => counter.value++, // โ
Direct modification
child: Text('Increment'),
),
ElevatedButton(
onPressed: () => isExpanded.toggle(), // โ
Use extensions
child: Text('Toggle'),
),
],
),
);
}
}
When to use:
- View-local state (toggles, local counters)
- Simple UI state
- Quick prototypes
- Single view usage
Pattern 2: Controller Pattern (Enforced Separation) #
Perfect for business logic, shared state, and team projects.
import 'package:swift_flutter/swift_flutter.dart';
// โ
Controller - only place where state can be modified
class CounterController extends SwiftController {
// Just use swift() - automatically read-only from views!
final counter = swift(0);
final name = swift('Hello');
void increment() => counter.value++; // โ
Works inside controller
void updateName(String n) => name.value = n;
}
// โ
View - can only read state and call controller methods
class MyView extends StatefulWidget {
@override
Widget build(BuildContext context) {
final controller = CounterController();
return Swift(
builder: (context) => Column(
children: [
Text('Count: ${controller.counter.value}'), // โ
Can read
Text('Name: ${controller.name.value}'),
ElevatedButton(
onPressed: controller.increment, // โ
Can call methods
child: Text('Increment'),
),
],
),
);
// โ controller.counter.value = 10; // Runtime error - cannot modify
}
}
When to use:
- Business logic & validation
- Shared state across multiple views
- Complex state management
- Team projects (enforced separation)
๐ Core Features #
1. Reactive State with swift() #
// Automatic type inference
final counter = swift(0); // SwiftValue<int>
final name = swift('Hello'); // SwiftValue<String>
final flag = swift(true); // SwiftValue<bool>
final price = swift(99.99); // SwiftValue<double>
// Explicit typing for custom types
final user = swift<User>(User('John'));
final list = swift<List<String>>(['a', 'b']);
// Use in widget - automatically rebuilds
Swift(
builder: (context) => Text('Count: ${counter.value}'),
)
// Update value - widget rebuilds automatically!
counter.value = 10;
2. Computed Values (Derived State) #
final price = swift(100.0);
final quantity = swift(2);
// Computed value - automatically updates when dependencies change
final total = Computed(() => price.value * quantity.value);
final summary = Computed(() => 'Total: \$${total.value.toStringAsFixed(2)}');
// Use in UI
Swift(
builder: (context) => Text(summary.value),
)
3. Async State Management (SwiftFuture) #
final dataFuture = SwiftFuture<String>();
// Execute async operation
await dataFuture.execute(() async {
await Future.delayed(Duration(seconds: 2));
return 'Data loaded!';
});
// Display state in UI
Swift(
builder: (context) => dataFuture.value.when(
idle: () => Text('Click to load'),
loading: () => CircularProgressIndicator(),
success: (data) => Text(data),
error: (error, stack) => Text('Error: $error'),
),
)
4. Form Validation (SwiftField) #
final emailField = SwiftField<String>('');
// Add validators
emailField.addValidator(Validators.required());
emailField.addValidator(Validators.email());
// Use in TextField
TextField(
onChanged: (value) => emailField.value = value,
decoration: InputDecoration(
errorText: emailField.error, // Shows validation error
),
)
// Check validation
if (emailField.isValid) {
// Proceed with form submission
}
5. Swift-like Extensions #
Bool Extensions:
bool flag = true;
flag = flag.toggle(); // false
Int/Double Extensions:
int count = 10;
count = count.add(5); // 15
count = count.sub(3); // 12
count = count.mul(2); // 24
double result = count.div(3); // 8.0
// Percentage operations
double price = 100.0;
price = price.applyPercent(20); // 120.0 (add 20%)
price = price.discount(10); // 108.0 (subtract 10%)
price = price.tax(5); // 113.4 (add 5% tax)
price = price.addGST(10); // 124.74 (add 10% GST)
// Range operations
count = count.clamped(min: 0, max: 100); // Clamp between 0-100
bool inRange = count.isBetween(0, 100); // Check if in range
// Formatting
String formatted = count.toCurrency(); // '$10.00'
String readable = 1500.toReadable(); // '1.5K'
String inr = 10000.toINR(); // 'โน10,000'
String Extensions:
String name = 'hello';
name = name.capitalized; // 'Hello'
name = name.add(' world'); // 'Hello world'
name = name.dropFirst(); // 'ello world'
name = name.dropLast(); // 'ello worl'
bool isEmpty = name.isEmpty; // false
bool isNotEmpty = name.isNotEmpty; // true
List Extensions:
List<int> numbers = [1, 2, 3, 4, 5];
numbers = numbers.dropFirst(); // [2, 3, 4, 5]
numbers = numbers.dropLast(); // [2, 3, 4]
bool found = numbers.containsWhere((n) => n > 3); // true
Reactive Extensions (on SwiftValue):
// All extensions work on SwiftValue too!
final counter = swift(10);
counter.add(5); // counter.value is now 15
counter.sub(3); // counter.value is now 12
counter.mul(2); // counter.value is now 24
final price = swift(100.0);
price.applyPercent(20); // price.value is now 120.0
price.discount(10); // price.value is now 108.0
price.tax(5); // price.value is now 113.4
final name = swift('hello');
name.add(' world'); // name.value is now 'hello world'
name.capitalized; // Returns 'Hello world' (read-only)
name.dropFirst(); // Returns 'ello world' (read-only)
final flag = swift(true);
flag.toggle(); // flag.value is now false
final numbers = swift<List<int>>([1, 2, 3]);
numbers.dropFirst(); // Returns [2, 3] (read-only)
๐ฆ Import Extensions:
import 'package:swift_flutter/swift_flutter.dart';
import 'package:swift_flutter/core/extensions.dart'; // Optional - for extensions
Available Extensions (80+ methods):
- Bool:
toggle() - Int/Double:
add(),sub(),mul(),div(),percent(),applyPercent(),discount(),tax(),addGST(),removeGST(),clamped(),isBetween(),lerp(),mapRange(),toCurrency(),toINR(),toReadable(),toOrdinal(), and more - String:
capitalized,add(),dropFirst(),dropLast(),isEmpty,isNotEmpty, and more - List:
dropFirst(),dropLast(),containsWhere(), and more - Iterable:
containsWhere(), and more - SwiftValue: All above methods work on reactive values too!
6. Batch Updates (Transactions) #
final x = swift(0);
final y = swift(0);
final z = swift(0);
// Batch multiple updates - only one rebuild occurs
Transaction.run(() {
x.value = 10;
y.value = 20;
z.value = 30;
});
7. Reactive Animations (SwiftTween) #
late final SwiftTween<double> sizeTween;
@override
void initState() {
super.initState();
sizeTween = SwiftTween<double>(
Tween(begin: 50.0, end: 200.0),
vsync: this, // Use AnimationController for better performance
);
}
// Animate to target value
await sizeTween.animateTo(1.0, duration: Duration(seconds: 1));
// Use in UI
Swift(
builder: (context) => Container(
width: sizeTween.value,
height: sizeTween.value,
),
)
8. Persistence (SwiftPersisted) #
final storage = MemoryStorage(); // Use SharedPreferences in production
late final SwiftPersisted<int> counter;
@override
void initState() {
super.initState();
counter = SwiftPersisted<int>(0, 'counter_key', storage);
}
// Value is automatically saved when changed
counter.value = 42; // Saved automatically
// Value is automatically loaded on initialization
Swift(
builder: (context) => Text('Counter: ${counter.value}'),
)
9. Global Store / Dependency Injection #
// Register a service
class UserService {
String getUser() => 'John Doe';
}
store.register<UserService>(UserService());
// Register reactive state
final globalCounter = swift(0);
store.registerState('counter', globalCounter);
// Retrieve service
final userService = store.get<UserService>();
print(userService.getUser()); // 'John Doe'
// Retrieve state
final counter = store.getState<int>('counter');
print(counter.value); // 0
10. Redux-like State Management #
// Define actions
class IncrementAction implements Action {
@override
String get type => 'INCREMENT';
}
// Define reducer
final counterReducer = (int state, Action action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
default:
return state;
}
};
// Create store
final counterStore = ReduxStore<int>(0, counterReducer);
// Use in widget
Swift(
builder: (context) => Text('Count: ${counterStore.value}'),
)
// Dispatch actions
counterStore.dispatch(IncrementAction());
๐ฏ Complete Examples #
Example 1: Direct Pattern with Extensions #
class CounterWidget extends StatefulWidget {
@override
Widget build(BuildContext context) {
final counter = swift(0);
final isExpanded = swift(false);
return Swift(
builder: (context) => Column(
children: [
Text('Count: ${counter.value}'),
ElevatedButton(
onPressed: () => counter.add(1), // โ
Use extension
child: Text('Add'),
),
ElevatedButton(
onPressed: () => counter.sub(1), // โ
Use extension
child: Text('Subtract'),
),
ElevatedButton(
onPressed: () => isExpanded.toggle(), // โ
Use extension
child: Text(isExpanded.value ? 'Collapse' : 'Expand'),
),
],
),
);
}
}
Example 2: Controller Pattern #
class ShoppingCartController extends SwiftController {
final items = swift<List<Item>>([]);
final total = swift(0.0);
late final Computed<double> tax;
late final Computed<double> grandTotal;
ShoppingCartController() {
tax = Computed(() => total.value * 0.1);
grandTotal = Computed(() => total.value + tax.value);
}
void addItem(Item item) {
items.value = [...items.value, item];
total.value = items.value.fold(0.0, (sum, item) => sum + item.price);
}
void removeItem(String itemId) {
items.value = items.value.where((item) => item.id != itemId).toList();
total.value = items.value.fold(0.0, (sum, item) => sum + item.price);
}
}
class ShoppingCartView extends StatefulWidget {
@override
Widget build(BuildContext context) {
final controller = ShoppingCartController();
return Swift(
builder: (context) => Column(
children: [
Text('Items: ${controller.items.value.length}'),
Text('Total: \$${controller.total.value.toStringAsFixed(2)}'),
Text('Tax: \$${controller.tax.value.toStringAsFixed(2)}'),
Text('Grand Total: \$${controller.grandTotal.value.toStringAsFixed(2)}'),
ElevatedButton(
onPressed: () => controller.addItem(Item(id: '1', price: 10.0)),
child: Text('Add Item'),
),
],
),
);
}
}
๐ฌ SwiftUI-like Declarative Animations #
Zero boilerplate - no controllers, no ticker providers needed!
Create smooth, professional animations with a simple chaining API. Works with just StatefulWidget and StatelessWidget - no mixins required!
Basic Usage #
import 'package:swift_flutter/swift_flutter.dart';
Container(
width: 100,
height: 100,
color: Colors.blue,
)
.animate()
.scale(1.2)
.fadeIn()
.duration(500.ms) // Shorthand: 500.ms or "500ms" or Duration(milliseconds: 500)
.repeat(reverse: true)
Available Animations #
Transformations:
.scale(value)- Scale uniformly.scaleX(value)- Scale on X axis.scaleY(value)- Scale on Y axis.rotate(degrees)- Rotate by degrees
Opacity:
.fadeIn()- Fade from 0 to 1.fadeOut()- Fade from 1 to 0.opacity(value)- Custom opacity (0.0 to 1.0)
Slides:
.slideX(value)- Slide on X axis.slideY(value)- Slide on Y axis.slideInTop()- Slide in from top.slideInBottom()- Slide in from bottom.slideInLeft()- Slide in from left.slideInRight()- Slide in from right
Special Effects:
.bounce()- Bounce animation curve.pulse()- Pulse animation curve.fadeInScale(value)- Combined fade and scale
Configuration:
.duration(value)- Animation duration (supports.500ms,0.5.s,5.m, orDuration).delay(value)- Animation delay.curve(curve)- Custom animation curve.repeat(reverse: bool)- Infinite repeat.repeatCount(count, reverse: bool)- Repeat specific times.persist()- Keep animation state on rebuild
Shorthand Duration Examples #
// String format
.animate().duration(".500ms")
.animate().duration("0.5s")
.animate().duration("5m")
// Number extensions (recommended)
.animate().duration(500.ms)
.animate().duration(0.5.s)
.animate().duration(5.m)
// Traditional Duration
.animate().duration(const Duration(seconds: 5))
Complex Example #
Container(
width: 120,
height: 120,
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(20),
),
)
.animate()
.fadeIn()
.scale(1.2)
.slideInBottom()
.rotate(180)
.duration(1.5.s)
.curve(Curves.easeInOut)
.repeat(reverse: true)
Why Mixin-Free? #
Traditional Flutter animations require SingleTickerProviderStateMixin or TickerProviderStateMixin in your widget class. With swift_flutter animations:
- โ
No mixins needed - Just use
StatefulWidgetorStatelessWidget - โ Zero boilerplate - No controller setup or disposal
- โ Clean API - Simple chaining like SwiftUI
- โ Automatic management - All ticker providers handled internally
The library uses TickerProviderStateMixin internally, but you never see it - it's completely hidden from your code!
๐ Debug Tool #
Swift Flutter includes a powerful debug tool for monitoring network requests, WebSocket connections, and logs. Enable it with a single line:
void main() {
SwiftFlutter.init(debugtool: true);
runApp(MyApp());
}
Features #
- Network Interceptor - Automatically captures all HTTP requests made with
httpanddiopackages - WebSocket Interceptor - Tracks WebSocket connections, messages, and events
- Log Interceptor - Captures all print statements and logs
- Curl Generator - Automatically generates curl commands for API testing
- Modern UI - Beautiful, responsive interface that works on all devices
- Zero Dependencies - Works without any external packages
Usage #
import 'package:swift_flutter/swift_flutter.dart';
import 'package:http/http.dart' as http;
// For MaterialApp
void main() {
SwiftFlutter.init(debugtool: true);
runApp(MyApp());
}
// For GetMaterialApp (requires navigatorKey)
void main() {
final navigatorKey = GlobalKey<NavigatorState>();
SwiftFlutter.init(
debugtool: true,
navigatorKey: navigatorKey, // Required for GetMaterialApp
);
runApp(GetMaterialApp(
navigatorKey: navigatorKey,
home: MyHomePage(),
));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('My App')),
body: Center(child: Text('Hello World')),
// โ ๏ธ IMPORTANT: Add SwiftDebugFloatingButton to access debug inspector
floatingActionButton: const SwiftDebugFloatingButton(),
);
}
}
// For GetMaterialApp with Stack in builder:
final navigatorKey = GlobalKey<NavigatorState>();
void main() {
// Pass navigatorKey to SwiftFlutter.init() for GetMaterialApp support
SwiftFlutter.init(
debugtool: true,
navigatorKey: navigatorKey, // Required for GetMaterialApp
);
runApp(GetMaterialApp(
navigatorKey: navigatorKey,
builder: (context, child) {
return Stack(
children: [
child!,
// SwiftDebugFloatingButton works in Stack!
// It will automatically use the navigatorKey from SwiftFlutter.init()
const SwiftDebugFloatingButton(),
],
);
},
));
}
// Make HTTP requests - automatically intercepted!
final response = await SwiftHttpHelper.intercept(
() => http.get(Uri.parse('https://api.example.com/data')),
method: 'GET',
url: 'https://api.example.com/data',
);
// Use swiftPrint() for log interception
swiftPrint('This will appear in the debug tool');
โ ๏ธ Important: You must add SwiftDebugFloatingButton to your Scaffold's floatingActionButton property to access the debug inspector page. The button will automatically appear when the debug tool is enabled.
The debug tool provides:
- HTTP Tab: View all HTTP requests with request/response details
- WebSocket Tab: Monitor WebSocket connections and messages
- Logs Tab: Browse all captured logs with filtering
- Curl Tab: Copy curl commands for API testing
Learn more at swiftflutter.com
๐ Documentation #
- Full API Documentation
- Learn more at swiftflutter.com
- Architecture Review
- Advanced Patterns & Best Practices
- Library Review
- DevTools Integration
๐งช Testing #
All features include comprehensive test coverage:
flutter test
400+ tests passing โ
๐ Example App #
See the example directory for a complete Flutter app demonstrating all features with interactive examples.
Run the example:
cd example
flutter run
๐ค Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
๐ License #
This project is licensed under the MIT License - see the LICENSE file for details.
๐ค Author #
โญ Support #
If you find this package useful, please consider giving it a โญ on pub.dev and GitHub!