minix 1.2.0 copy "minix: ^1.2.0" to clipboard
minix: ^1.2.0 copied to clipboard

Reactive state management for Flutter with form validation, collection observables, async operations, dependency injection, and automatic memory management. Zero dependencies, high performance, and co [...]

example/main.dart

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

void main() {
  // Enable DI logging for development
  Injector.enableLogs = true;

  // Register dependencies
  _setupDependencies();

  runApp(const MyApp());
}

void _setupDependencies() {
  // Register services
  Injector.put<CounterService>(CounterService());
  Injector.lazyPut<ApiService>(() => ApiService());

  // Register ViewModels
  Injector.lazyPut<CounterViewModel>(() => CounterViewModel());
  Injector.lazyPut<UserViewModel>(() => UserViewModel());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Minix Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const HomeScreen(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Minix Examples')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _buildExampleCard(
            'Basic Counter',
            'Simple reactive counter with Observer',
            () => Navigator.push(
                context, MaterialPageRoute(builder: (_) => CounterScreen())),
          ),
          _buildExampleCard(
            'Async Operations',
            'Loading states with AsyncObservable',
            () => Navigator.push(
                context, MaterialPageRoute(builder: (_) => AsyncScreen())),
          ),
          _buildExampleCard(
            'Stream Example',
            'Real-time data with StreamObservable',
            () => Navigator.push(
                context, MaterialPageRoute(builder: (_) => StreamScreen())),
          ),
          _buildExampleCard(
            'MVVM Pattern',
            'Architecture example with DI',
            () => Navigator.push(
                context, MaterialPageRoute(builder: (_) => const MVVMScreen())),
          ),
        ],
      ),
    );
  }

  Widget _buildExampleCard(String title, String subtitle, VoidCallback onTap) {
    return Card(
      margin: const EdgeInsets.only(bottom: 16),
      child: ListTile(
        title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
        subtitle: Text(subtitle),
        trailing: const Icon(Icons.arrow_forward_ios),
        onTap: onTap,
      ),
    );
  }
}

// =============================================================================
// BASIC COUNTER EXAMPLE
// =============================================================================

class CounterScreen extends StatelessWidget {
  CounterScreen({super.key});
  final counter = Observable(0);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Basic Counter')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('Counter Value:'),
            const SizedBox(height: 16),
            // Observer automatically rebuilds when counter.value changes
            Observer(() {
              return Text(
                '${counter.value}',
              );
            }),
            const SizedBox(height: 32),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                  onPressed: () => counter.value--,
                  child: const Text('-'),
                ),
                ElevatedButton(
                  onPressed: () => counter.value = 0,
                  child: const Text('Reset'),
                ),
                ElevatedButton(
                  onPressed: () => counter.value++,
                  child: const Text('+'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

// =============================================================================
// ASYNC OPERATIONS EXAMPLE
// =============================================================================

class AsyncScreen extends StatelessWidget {
  AsyncScreen({super.key});
  final userAsync = AsyncObservable<User>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Async Operations')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            ElevatedButton(
              onPressed: () => _loadUser(),
              child: const Text('Load User Data'),
            ),
            const SizedBox(height: 20),
            Expanded(
              child: Observer(() {
                // Reactive UI based on async state
                if (userAsync.isLoading) {
                  return const Center(
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        CircularProgressIndicator(),
                        SizedBox(height: 16),
                        Text('Loading user data...'),
                      ],
                    ),
                  );
                }

                if (userAsync.hasError) {
                  return Center(
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        const Icon(Icons.error, color: Colors.red, size: 64),
                        const SizedBox(height: 16),
                        Text(
                          'Error: ${userAsync.error}',
                          style: const TextStyle(color: Colors.red),
                          textAlign: TextAlign.center,
                        ),
                        const SizedBox(height: 16),
                        ElevatedButton(
                          onPressed: () => _loadUser(),
                          child: const Text('Retry'),
                        ),
                      ],
                    ),
                  );
                }

                if (userAsync.hasData) {
                  final user = userAsync.data!;
                  return Center(
                    child: Card(
                      child: Padding(
                        padding: const EdgeInsets.all(16),
                        child: Column(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            CircleAvatar(
                              radius: 50,
                              child: Text(user.name[0]),
                            ),
                            const SizedBox(height: 16),
                            Text(
                              user.name,
                            ),
                            Text(user.email),
                            const SizedBox(height: 16),
                            Text(
                              'ID: ${user.id}',
                              style: const TextStyle(color: Colors.grey),
                            ),
                          ],
                        ),
                      ),
                    ),
                  );
                }

                return const Center(
                  child: Text('No data loaded yet'),
                );
              }),
            ),
          ],
        ),
      ),
    );
  }

  Future<void> _loadUser() async {
    await userAsync.execute(() async {
      // Simulate API call
      await Future.delayed(const Duration(seconds: 2));

      // Simulate random success/error
      if (DateTime.now().millisecond % 3 == 0) {
        throw Exception('Network error occurred');
      }

      return User(
        id: '${DateTime.now().millisecond}',
        name: 'John Doe',
        email: 'john.doe@example.com',
      );
    });
  }
}

// =============================================================================
// STREAM EXAMPLE
// =============================================================================

class StreamScreen extends StatelessWidget {
  StreamScreen({super.key});
  final messageStream = StreamObservable<String>();
  final messages = Observable<List<String>>([]);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Stream Example')),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Row(
              children: [
                Expanded(
                  child: ElevatedButton(
                    onPressed: () => _startStream(),
                    child: const Text('Start Stream'),
                  ),
                ),
                const SizedBox(width: 8),
                Expanded(
                  child: ElevatedButton(
                    onPressed: () => _stopStream(),
                    child: const Text('Stop Stream'),
                  ),
                ),
              ],
            ),
          ),
          Expanded(
            child: Observer(() {
              return ListView.builder(
                itemCount: messages.value.length,
                itemBuilder: (context, index) {
                  final message = messages.value[index];
                  return ListTile(
                    leading: const Icon(Icons.message),
                    title: Text(message),
                    subtitle: Text(
                        'Received at ${DateTime.now().toString().substring(11, 19)}'),
                  );
                },
              );
            }),
          ),
          Observer(() {
            return Container(
              padding: const EdgeInsets.all(16),
              color: messageStream.isListening
                  ? Colors.green[100]
                  : Colors.grey[100],
              child: Row(
                children: [
                  Icon(
                    messageStream.isListening
                        ? Icons.radio_button_checked
                        : Icons.radio_button_off,
                    color:
                        messageStream.isListening ? Colors.green : Colors.grey,
                  ),
                  const SizedBox(width: 8),
                  Text(
                    messageStream.isListening
                        ? 'Stream Active'
                        : 'Stream Inactive',
                    style: TextStyle(
                      color: messageStream.isListening
                          ? Colors.green[800]
                          : Colors.grey[800],
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ],
              ),
            );
          }),
        ],
      ),
    );
  }

  void _startStream() {
    // Create a sample stream
    final stream = Stream.periodic(
      const Duration(seconds: 1),
      (index) =>
          'Message ${index + 1}: ${DateTime.now().toString().substring(11, 19)}',
    );

    messageStream.listen(
      stream,
      onData: (message) {
        // Add to messages list
        final currentMessages = List<String>.from(messages.value);
        currentMessages.insert(0, message);

        // Keep only last 10 messages
        if (currentMessages.length > 10) {
          currentMessages.removeLast();
        }

        messages.value = currentMessages;
      },
    );
  }

  Future<void> _stopStream() async {
    await messageStream.cancel();
  }
}

// =============================================================================
// MVVM PATTERN EXAMPLE
// =============================================================================

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

  @override
  Widget build(BuildContext context) {
    final viewModel = Injector.find<CounterViewModel>();

    return Scaffold(
      appBar: AppBar(title: const Text('MVVM Pattern')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Observer(() {
              return Text(
                'Counter: ${viewModel.counter}',
              );
            }),
            const SizedBox(height: 16),
            Observer(() {
              return Text(
                'Status: ${viewModel.status}',
                style: TextStyle(
                  color: viewModel.isEven ? Colors.green : Colors.orange,
                  fontWeight: FontWeight.bold,
                ),
              );
            }),
            const SizedBox(height: 32),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                  onPressed: viewModel.decrement,
                  child: const Text('- Decrement'),
                ),
                ElevatedButton(
                  onPressed: viewModel.reset,
                  child: const Text('Reset'),
                ),
                ElevatedButton(
                  onPressed: viewModel.increment,
                  child: const Text('+ Increment'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

// =============================================================================
// MODELS & SERVICES
// =============================================================================

class User {
  User({required this.id, required this.name, required this.email});
  final String id;
  final String name;
  final String email;
}

class CounterService {
  final _counter = Observable(0);

  int get counter => _counter.value;
  Observable<int> get counterObservable => _counter;

  void increment() => _counter.value++;
  void decrement() => _counter.value--;
  void reset() => _counter.value = 0;
}

class ApiService {
  Future<User> fetchUser(String id) async {
    await Future.delayed(const Duration(seconds: 1));
    return User(id: id, name: 'API User', email: 'api@example.com');
  }
}

class CounterViewModel {
  final CounterService _service = Injector.find<CounterService>();

  int get counter => _service.counter;

  String get status => isEven ? 'Even Number' : 'Odd Number';

  bool get isEven => _service.counter % 2 == 0;

  void increment() => _service.increment();
  void decrement() => _service.decrement();
  void reset() => _service.reset();
}

class UserViewModel {
  final _user = AsyncObservable<User>();
  final ApiService _api = Injector.find<ApiService>();

  AsyncObservable<User> get user => _user;

  Future<void> loadUser(String id) async {
    await _user.execute(() => _api.fetchUser(id));
  }
}
2
likes
130
points
33
downloads

Publisher

unverified uploader

Weekly Downloads

Reactive state management for Flutter with form validation, collection observables, async operations, dependency injection, and automatic memory management. Zero dependencies, high performance, and comprehensive testing support for scalable applications.

Repository (GitHub)
View/report issues

Topics

#state-management #dependency-injection #reactive-programming #flutter #observables

Documentation

API reference

License

MIT (license)

Dependencies

flutter, intl

More

Packages that depend on minix