swift_flutter 1.1.1 copy "swift_flutter: ^1.1.1" to clipboard
swift_flutter: ^1.1.1 copied to clipboard

A reactive state management library for Flutter with automatic dependency tracking, computed values, async state, form validation, and more.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:swift_flutter/swift_flutter.dart';

void main() {
  // Enable logger for debugging
  Logger.setEnabled(true);
  Logger.setLevel(LogLevel.debug);
  
  // Register middleware
  store.addMiddleware(LoggingMiddleware());
  
  runApp(const SwiftFlutterExampleApp());
}

class SwiftFlutterExampleApp extends StatelessWidget {
  const SwiftFlutterExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Swift Flutter Examples',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: const ExampleHomePage(),
    );
  }
}

class ExampleHomePage extends StatelessWidget {
  const ExampleHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Swift Flutter - All Features'),
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _buildFeatureCard(
            context,
            '1. Reactive State (Rx)',
            const RxExample(),
            Colors.blue,
          ),
          _buildFeatureCard(
            context,
            '2. Computed (Derived State)',
            const ComputedExample(),
            Colors.green,
          ),
          _buildFeatureCard(
            context,
            '3. SwiftFuture (Async State)',
            const SwiftFutureExample(),
            Colors.orange,
          ),
          _buildFeatureCard(
            context,
            '4. Form Validation',
            const FormValidationExample(),
            Colors.purple,
          ),
          _buildFeatureCard(
            context,
            '5. Transactions (Batch Updates)',
            const TransactionExample(),
            Colors.red,
          ),
          _buildFeatureCard(
            context,
            '6. Animation Tween',
            const TweenExample(),
            Colors.pink,
          ),
          _buildFeatureCard(
            context,
            '7. Lifecycle Controller',
            const LifecycleExample(),
            Colors.teal,
          ),
          _buildFeatureCard(
            context,
            '8. Persistence',
            const PersistenceExample(),
            Colors.indigo,
          ),
          _buildFeatureCard(
            context,
            '9. Global Store / DI',
            const StoreExample(),
            Colors.amber,
          ),
          _buildFeatureCard(
            context,
            '10. Logger',
            const LoggerExample(),
            Colors.cyan,
          ),
        ],
      ),
    );
  }

  Widget _buildFeatureCard(
    BuildContext context,
    String title,
    Widget example,
    Color color,
  ) {
    return Card(
      margin: const EdgeInsets.only(bottom: 16),
      elevation: 4,
      child: ExpansionTile(
        leading: CircleAvatar(backgroundColor: color),
        title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: example,
          ),
        ],
      ),
    );
  }
}

// =======================================================
// 1. REACTIVE STATE (Rx) EXAMPLE
// =======================================================

class RxExample extends StatefulWidget {
  const RxExample({super.key});

  @override
  State<RxExample> createState() => _RxExampleState();
}

class _RxExampleState extends State<RxExample> {
  // Using swift() helper for automatic type inference
  // swift() creates Rx<T> instances - cleaner than Rx<int>(0)
  final counter = swift(0);  // Automatically inferred as Rx<int>
  final name = swift('Swift Flutter');  // Automatically inferred as Rx<String>
  
  // Or use explicit typing if you prefer:
  // final counter = swift<int>(0);
  // final name = swift<String>('Swift Flutter');
  
  // Note: SwiftFuture, SwiftField, SwiftTween, SwiftPersisted are different classes
  // They are specialized wrappers, not Rx<T> instances
  // They use the Swift prefix to match the library naming convention

  @override
  void dispose() {
    counter.dispose();
    name.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Mark(
          builder: (context) => Text(
            'Counter: ${counter.value}',
            style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
          ),
        ),
        const SizedBox(height: 8),
        Mark(
          builder: (context) => Text(
            'Name: ${name.value}',
            style: const TextStyle(fontSize: 18),
          ),
        ),
        const SizedBox(height: 16),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            ElevatedButton(
              onPressed: () => counter.value--,
              child: const Text('-'),
            ),
            ElevatedButton(
              onPressed: () => counter.value++,
              child: const Text('+'),
            ),
            ElevatedButton(
              onPressed: () => name.value = 'Updated: ${DateTime.now().second}',
              child: const Text('Update Name'),
            ),
          ],
        ),
      ],
    );
  }
}

// =======================================================
// 2. COMPUTED (DERIVED STATE) EXAMPLE
// =======================================================

class ComputedExample extends StatefulWidget {
  const ComputedExample({super.key});

  @override
  State<ComputedExample> createState() => _ComputedExampleState();
}

class _ComputedExampleState extends State<ComputedExample> {
  // Automatic type inference
  final price = swift(100.0);  // Automatically inferred as Rx<double>
  final quantity = swift(2);  // Automatically inferred as Rx<int>
  
  // Or use explicit typing:
  // final price = swift<double>(100.0);
  // final quantity = swift<int>(2);
  late final Computed<double> total;
  late final Computed<String> summary;

  @override
  void initState() {
    super.initState();
    total = Computed(() => price.value * quantity.value);
    summary = Computed(() => 'Total: \$${total.value.toStringAsFixed(2)}');
  }

  @override
  void dispose() {
    total.dispose();
    summary.dispose();
    price.dispose();
    quantity.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            Expanded(
              child: Column(
                children: [
                  const Text('Price:'),
                  Mark(
                    builder: (context) => Slider(
                      value: price.value,
                      min: 0,
                      max: 200,
                      onChanged: (v) => price.value = v,
                    ),
                  ),
                  Mark(
                    builder: (context) => Text('\$${price.value.toStringAsFixed(2)}'),
                  ),
                ],
              ),
            ),
            const SizedBox(width: 16),
            Expanded(
              child: Column(
                children: [
                  const Text('Quantity:'),
                  Mark(
                    builder: (context) => Slider(
                      value: quantity.value.toDouble(),
                      min: 0,
                      max: 10,
                      divisions: 10,
                      onChanged: (v) => quantity.value = v.toInt(),
                    ),
                  ),
                  Mark(
                    builder: (context) => Text('${quantity.value}'),
                  ),
                ],
              ),
            ),
          ],
        ),
        const SizedBox(height: 16),
        Mark(
          builder: (context) => Container(
            padding: const EdgeInsets.all(16),
            decoration: BoxDecoration(
              color: Colors.green.shade100,
              borderRadius: BorderRadius.circular(8),
            ),
            child: Text(
              summary.value,
              style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
            ),
          ),
        ),
      ],
    );
  }
}

// =======================================================
// 3. RXFUTURE (ASYNC STATE) EXAMPLE
// =======================================================

class SwiftFutureExample extends StatefulWidget {
  const SwiftFutureExample({super.key});

  @override
  State<SwiftFutureExample> createState() => _SwiftFutureExampleState();
}

class _SwiftFutureExampleState extends State<SwiftFutureExample> {
  // SwiftFuture is a specialized class for async operations
  // It's different from Rx<T> - it manages loading/error/success states
  final swiftFuture = SwiftFuture<String>();

  Future<String> _fetchData() async {
    await Future.delayed(const Duration(seconds: 2));
    // Always return success for demo
    return 'Success! Data loaded at ${DateTime.now().toString().substring(11, 19)}';
  }
  
  Future<String> _fetchDataWithError() async {
    await Future.delayed(const Duration(seconds: 2));
    throw Exception('Simulated error for testing');
  }

  @override
  void dispose() {
    swiftFuture.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Mark(
          builder: (context) => swiftFuture.value.when(
            idle: () => const Text('Click to load data', style: TextStyle(fontSize: 16)),
            loading: () => const Row(
              children: [
                SizedBox(
                  width: 20,
                  height: 20,
                  child: CircularProgressIndicator(strokeWidth: 2),
                ),
                SizedBox(width: 8),
                Text('Loading...', style: TextStyle(fontSize: 16)),
              ],
            ),
            success: (data) => Container(
              padding: const EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Colors.green.shade100,
                borderRadius: BorderRadius.circular(8),
              ),
              child: Text(data, style: const TextStyle(fontSize: 16)),
            ),
            error: (error, stack) => Container(
              padding: const EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Colors.red.shade100,
                borderRadius: BorderRadius.circular(8),
              ),
              child: Text('Error: $error', style: const TextStyle(fontSize: 16)),
            ),
          ),
        ),
        const SizedBox(height: 16),
        Row(
          children: [
            ElevatedButton(
              onPressed: () => swiftFuture.execute(_fetchData),
              child: const Text('Load Data'),
            ),
            const SizedBox(width: 8),
            ElevatedButton(
              onPressed: () => swiftFuture.execute(_fetchDataWithError),
              child: const Text('Test Error'),
            ),
            const SizedBox(width: 8),
            ElevatedButton(
              onPressed: () => swiftFuture.reset(),
              child: const Text('Reset'),
            ),
          ],
        ),
      ],
    );
  }
}

// =======================================================
// 4. FORM VALIDATION EXAMPLE
// =======================================================

class FormValidationExample extends StatefulWidget {
  const FormValidationExample({super.key});

  @override
  State<FormValidationExample> createState() => _FormValidationExampleState();
}

class _FormValidationExampleState extends State<FormValidationExample> {
  // SwiftField is a specialized class for form validation
  // It extends Rx<T> with validation capabilities
  final emailField = SwiftField<String>('');
  final passwordField = SwiftField<String>('');

  @override
  void initState() {
    super.initState();
    emailField.addValidator(Validators.required('Email is required'));
    emailField.addValidator(Validators.email('Invalid email format'));
    passwordField.addValidator(Validators.required('Password is required'));
    passwordField.addValidator(Validators.minLength(6, 'Password must be at least 6 characters'));
  }

  @override
  void dispose() {
    emailField.dispose();
    passwordField.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        TextField(
          decoration: InputDecoration(
            labelText: 'Email',
            errorText: emailField.error,
            border: const OutlineInputBorder(),
          ),
          onChanged: (value) {
            emailField.value = value;
            if (emailField.touched) {
              emailField.validate();
            }
          },
          onTap: () => emailField.markAsTouched(),
        ),
        const SizedBox(height: 16),
        TextField(
          decoration: InputDecoration(
            labelText: 'Password',
            errorText: passwordField.error,
            border: const OutlineInputBorder(),
          ),
          obscureText: true,
          onChanged: (value) {
            passwordField.value = value;
            if (passwordField.touched) {
              passwordField.validate();
            }
          },
          onTap: () => passwordField.markAsTouched(),
        ),
        const SizedBox(height: 16),
        Mark(
          builder: (context) => ElevatedButton(
            onPressed: emailField.isValid && passwordField.isValid
                ? () {
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(content: Text('Form is valid!')),
                    );
                  }
                : null,
            child: const Text('Submit'),
          ),
        ),
      ],
    );
  }
}

// =======================================================
// 5. TRANSACTION (BATCH UPDATES) EXAMPLE
// =======================================================

class TransactionExample extends StatefulWidget {
  const TransactionExample({super.key});

  @override
  State<TransactionExample> createState() => _TransactionExampleState();
}

class _TransactionExampleState extends State<TransactionExample> {
  // Automatic type inference
  final x = swift(0);
  final y = swift(0);
  final z = swift(0);
  int rebuildCount = 0;

  @override
  void initState() {
    super.initState();
    x.addListener(() => rebuildCount++);
    y.addListener(() => rebuildCount++);
    z.addListener(() => rebuildCount++);
  }

  @override
  void dispose() {
    x.dispose();
    y.dispose();
    z.dispose();
    super.dispose();
  }

  void _updateWithoutTransaction() {
    rebuildCount = 0;
    x.value = 10;
    y.value = 20;
    z.value = 30;
  }

  void _updateWithTransaction() {
    rebuildCount = 0;
    Transaction.run(() {
      x.value = 10;
      y.value = 20;
      z.value = 30;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Mark(
          builder: (context) => Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text('X: ${x.value}'),
              Text('Y: ${y.value}'),
              Text('Z: ${z.value}'),
              const SizedBox(height: 8),
              Text(
                'Rebuilds: $rebuildCount',
                style: TextStyle(
                  color: rebuildCount > 1 ? Colors.red : Colors.green,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
        ),
        const SizedBox(height: 16),
        Row(
          children: [
            ElevatedButton(
              onPressed: _updateWithoutTransaction,
              child: const Text('Update (No Transaction)'),
            ),
            const SizedBox(width: 8),
            ElevatedButton(
              onPressed: _updateWithTransaction,
              style: ElevatedButton.styleFrom(backgroundColor: Colors.green),
              child: const Text('Update (With Transaction)'),
            ),
          ],
        ),
        const SizedBox(height: 8),
        const Text(
          'Notice: Transaction batches updates into a single rebuild',
          style: TextStyle(fontSize: 12, fontStyle: FontStyle.italic),
        ),
      ],
    );
  }
}

// =======================================================
// 6. ANIMATION TWEEN EXAMPLE
// =======================================================

class TweenExample extends StatefulWidget {
  const TweenExample({super.key});

  @override
  State<TweenExample> createState() => _TweenExampleState();
}

class _TweenExampleState extends State<TweenExample> {
  // SwiftTween is a specialized class for reactive animations
  // It manages tween interpolation with reactive progress
  late final SwiftTween<double> sizeTween;
  late final SwiftTween<Color?> colorTween;

  @override
  void initState() {
    super.initState();
    sizeTween = TweenHelper.doubleTween(begin: 50.0, end: 200.0);
    colorTween = TweenHelper.colorTween(
      begin: Colors.blue,
      end: Colors.purple,
    );
  }

  @override
  void dispose() {
    sizeTween.dispose();
    colorTween.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Mark(
          builder: (context) => Center(
            child: Container(
              width: sizeTween.value,
              height: sizeTween.value,
              decoration: BoxDecoration(
                color: colorTween.value,
                shape: BoxShape.circle,
              ),
            ),
          ),
        ),
        const SizedBox(height: 16),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            ElevatedButton(
              onPressed: () => sizeTween.animateTo(1.0),
              child: const Text('Animate Size'),
            ),
            ElevatedButton(
              onPressed: () => colorTween.animateTo(1.0),
              child: const Text('Animate Color'),
            ),
          ],
        ),
        const SizedBox(height: 8),
        Mark(
          builder: (context) => Slider(
            value: sizeTween.progress,
            min: 0.0,
            max: 1.0,
            onChanged: (v) => sizeTween.progress = v,
          ),
        ),
        Mark(
          builder: (context) => Text('Progress: ${(sizeTween.progress * 100).toStringAsFixed(0)}%'),
        ),
      ],
    );
  }
}

// =======================================================
// 7. LIFECYCLE CONTROLLER EXAMPLE
// =======================================================

class LifecycleExample extends StatefulWidget {
  const LifecycleExample({super.key});

  @override
  State<LifecycleExample> createState() => _LifecycleExampleState();
}

class _LifecycleExampleState extends State<LifecycleExample> with LifecycleMixin {
  @override
  Widget build(BuildContext context) {
    return Mark(
      builder: (context) => Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            padding: const EdgeInsets.all(16),
            decoration: BoxDecoration(
              color: _getColorForState(),
              borderRadius: BorderRadius.circular(8),
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'State: ${lifecycle.state.name}',
                  style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 8),
                Text('Initialized: ${lifecycle.isInitialized}'),
                Text('Active: ${lifecycle.isActive}'),
                Text('Disposed: ${lifecycle.isDisposed}'),
              ],
            ),
          ),
          const SizedBox(height: 16),
          Row(
            children: [
              ElevatedButton(
                onPressed: () => lifecycle.activate(),
                child: const Text('Activate'),
              ),
              const SizedBox(width: 8),
              ElevatedButton(
                onPressed: () => lifecycle.pause(),
                child: const Text('Pause'),
              ),
              const SizedBox(width: 8),
              ElevatedButton(
                onPressed: () => lifecycle.resume(),
                child: const Text('Resume'),
              ),
            ],
          ),
        ],
      ),
    );
  }

  Color _getColorForState() {
    switch (lifecycle.state) {
      case LifecycleState.initializing:
        return Colors.grey.shade300;
      case LifecycleState.initialized:
        return Colors.blue.shade100;
      case LifecycleState.active:
        return Colors.green.shade100;
      case LifecycleState.paused:
        return Colors.orange.shade100;
      default:
        return Colors.red.shade100;
    }
  }
}

// =======================================================
// 8. PERSISTENCE EXAMPLE
// =======================================================

class PersistenceExample extends StatefulWidget {
  const PersistenceExample({super.key});

  @override
  State<PersistenceExample> createState() => _PersistenceExampleState();
}

class _PersistenceExampleState extends State<PersistenceExample> {
  // SwiftPersisted is a specialized class that extends Rx<T>
  // It automatically saves/loads values from storage
  late final SwiftPersisted<int> counter;
  final storage = MemoryStorage();

  @override
  void initState() {
    super.initState();
    // SwiftPersisted needs explicit type and storage configuration
    counter = SwiftPersisted<int>(0, 'example_counter', storage);
  }

  @override
  void dispose() {
    counter.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Mark(
          builder: (context) => Text(
            'Persisted Counter: ${counter.value}',
            style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
          ),
        ),
        const SizedBox(height: 16),
        const Text(
          'This value persists across app restarts (in memory for this example)',
          style: TextStyle(fontSize: 12, fontStyle: FontStyle.italic),
        ),
        const SizedBox(height: 16),
        Row(
          children: [
            ElevatedButton(
              onPressed: () => counter.value--,
              child: const Text('-'),
            ),
            const SizedBox(width: 8),
            ElevatedButton(
              onPressed: () => counter.value++,
              child: const Text('+'),
            ),
            const SizedBox(width: 8),
            ElevatedButton(
              onPressed: () => counter.clear(),
              child: const Text('Clear'),
            ),
          ],
        ),
      ],
    );
  }
}

// =======================================================
// 9. GLOBAL STORE / DEPENDENCY INJECTION EXAMPLE
// =======================================================

class StoreExample extends StatefulWidget {
  const StoreExample({super.key});

  @override
  State<StoreExample> createState() => _StoreExampleState();
}

class _StoreExampleState extends State<StoreExample> {
  @override
  void initState() {
    super.initState();
    // Register services
    store.register<UserService>(UserService());
    store.registerState('userCount', Rx<int>(0));
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        ElevatedButton(
          onPressed: () {
            final userService = store.get<UserService>();
            final count = store.getState<int>('userCount');
            count.value = userService.getUserCount();
            
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('User count: ${count.value}')),
            );
          },
          child: const Text('Get User Count from Store'),
        ),
        const SizedBox(height: 16),
        Mark(
          builder: (context) => Text(
            'User Count: ${store.getState<int>('userCount').value}',
            style: const TextStyle(fontSize: 18),
          ),
        ),
        const SizedBox(height: 8),
        ElevatedButton(
          onPressed: () async {
            final action = ExampleAction(
              'increment',
              {},
              () async => store.getState<int>('userCount').value++,
            );
            await store.dispatch(action);
          },
          child: const Text('Dispatch Action (with Middleware)'),
        ),
      ],
    );
  }
}

class UserService {
  int getUserCount() => 42;
}

// =======================================================
// 10. LOGGER EXAMPLE
// =======================================================

class LoggerExample extends StatefulWidget {
  const LoggerExample({super.key});

  @override
  State<LoggerExample> createState() => _LoggerExampleState();
}

class _LoggerExampleState extends State<LoggerExample> {
  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            ElevatedButton(
              onPressed: () {
                Logger.debug('Debug message');
                _showLogs(context);
              },
              child: const Text('Debug'),
            ),
            const SizedBox(width: 8),
            ElevatedButton(
              onPressed: () {
                Logger.info('Info message');
                _showLogs(context);
              },
              child: const Text('Info'),
            ),
            const SizedBox(width: 8),
            ElevatedButton(
              onPressed: () {
                Logger.warning('Warning message');
                _showLogs(context);
              },
              child: const Text('Warning'),
            ),
            const SizedBox(width: 8),
            ElevatedButton(
              onPressed: () {
                Logger.error('Error message');
                _showLogs(context);
              },
              child: const Text('Error'),
            ),
          ],
        ),
        const SizedBox(height: 16),
        ElevatedButton(
          onPressed: () {
            Logger.clear();
            _showLogs(context);
          },
          child: const Text('Clear Logs'),
        ),
        const SizedBox(height: 16),
        Container(
          padding: const EdgeInsets.all(12),
          decoration: BoxDecoration(
            color: Colors.grey.shade200,
            borderRadius: BorderRadius.circular(8),
          ),
          height: 150,
          child: SingleChildScrollView(
            child: Text(
              Logger.history.map((e) => e.toString()).join('\n'),
              style: const TextStyle(fontSize: 12, fontFamily: 'monospace'),
            ),
          ),
        ),
      ],
    );
  }

  void _showLogs(BuildContext context) {
    setState(() {});
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('Log history: ${Logger.history.length} entries'),
        duration: const Duration(seconds: 1),
      ),
    );
  }
}
15
likes
0
points
620
downloads

Publisher

verified publisherswiftflutter.com

Weekly Downloads

A reactive state management library for Flutter with automatic dependency tracking, computed values, async state, form validation, and more.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter

More

Packages that depend on swift_flutter