lifecycle_controller 0.0.1 copy "lifecycle_controller: ^0.0.1" to clipboard
lifecycle_controller: ^0.0.1 copied to clipboard

A Flutter library for simplified state and lifecycle management. Offers structured handling of screen events, local state, async operations, and UI updates, promoting clean architecture and maintainab [...]

LifecycleController #

Lifecycle Controller is a powerful Flutter library that streamlines state management and lifecycle handling in your applications. It provides a structured and intuitive approach to managing screen lifecycle events, local state, and UI updates, enabling you to build robust, scalable, and maintainable Flutter applications with ease.

🌟 Why Lifecycle Controller? #

Developing Flutter applications often involves managing complex state and lifecycle events. As applications grow, maintaining clean separation between UI and business logic becomes challenging. LifecycleController addresses these challenges by:

  • Simplifying State Management: Offers a clean, provider-based architecture for managing local state without boilerplate code.
  • Handling Lifecycle Events: Provides built-in methods to handle screen lifecycle events like navigation changes and app lifecycle states.
  • Asynchronous Operation Management: Integrates loading and error handling into async tasks seamlessly.
  • Enhancing Maintainability: Promotes separation of concerns, making your codebase cleaner and easier to maintain.
  • Customizable UI Components: Allows easy customization of loading and error screens to match your app's design.
  • Efficient Subscription Management: Simplifies stream subscription handling to prevent memory leaks.

🚀 Features #

  • 🔄 Lifecycle Management: Handle screen lifecycle events such as push, pop, and app state changes effortlessly.
  • 🧠 State Management: Manage local state with a clean, provider-based approach.
  • Async Operation Handling: Built-in support for managing loading states and errors during asynchronous tasks.
  • 🏗️ Separation of Concerns: Clear separation between UI and business logic for better scalability and maintainability.
  • 🎨 Customizable UI Components: Easily override default loading and error UIs to match your app's theme.
  • 🔔 Subscription Management: Simplify stream subscriptions with built-in methods to add and dispose of subscriptions properly.

📥 Installation #

Add LifecycleController to your pubspec.yaml file:

dependencies:
  lifecycle_controller: latest_version

Then run:

flutter pub get

🛠️ Getting Started #

Step 1: Create a Controller #

Create a controller by extending LifecycleController. This controller will manage the state and logic for your screen.

import 'package:lifecycle_screen/lifecycle_screen.dart';

class CounterController extends LifecycleController {
  int _counter = 0;

  int get counter => _counter;

  void increment() {
    // Use `asyncRun` to handle loading states and errors automatically
    asyncRun(() async {
      await Future.delayed(const Duration(seconds: 1));
      _counter++;
      notifyListeners(); // Notify listeners to rebuild UI
    });
  }

  @override
  void onInit() {
    super.onInit();
    // Initialization logic here
    print('CounterController initialized');
  }

  @override
  void onDispose() {
    super.onDispose();
    // Cleanup logic here
    print('CounterController disposed');
  }
}

Step 2: Create a Screen Widget #

Create a screen by extending LifecycleWidget, linking it with your controller, and building your UI.

import 'package:flutter/material.dart';
import 'package:lifecycle_screen/lifecycle_screen.dart';
import 'package:provider/provider.dart';

class CounterScreen extends LifecycleWidget<CounterController> {
  const CounterScreen({Key? key}) : super(key: key);

  @override
  CounterController createController() => CounterController();

  @override
  Widget buildView(BuildContext context, CounterController controller) {
    final counter = context.select<CounterController, int>(
      (controller) => controller.counter,
    );

    return Scaffold(
      appBar: AppBar(title: const Text('Counter Example')),
      body: Center(
        child: Text(
          'Count: $counter',
          style: Theme.of(context).textTheme.headlineMedium,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

Step 3: Integrate into Your App #

Use your screen in the app and set up the navigatorObservers to enable lifecycle event tracking.

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // Initialize the RouteObserver
  static final RouteObserver<PageRoute> routeObserver =
      LifecycleController.basePageRouteObserver;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'LifecycleWidget Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const CounterScreen(),
      navigatorObservers: [routeObserver],
    );
  }
}

📖 Detailed Guide #

State Management with Provider #

LifecycleController leverages the power of the Provider package for state management. It automatically sets up the necessary Provider infrastructure, allowing you to access your controller's state easily within your screen.

Accessing Controller State

Access your controller and its state using context.read, context.watch, or context.select:

// Read the controller instance (does not listen for changes)
final controller = context.read<CounterController>();

// Watch for all changes in the controller (rebuilds on any change)
final counter = context.watch<CounterController>().counter;

// Select and listen to specific properties (optimal for performance)
final counter = context.select<CounterController, int>(
  (controller) => controller.counter,
);

💡 Tip: Use context.select when you want to listen to specific properties to optimize performance and prevent unnecessary rebuilds.

Lifecycle Hooks #

LifecycleController provides several lifecycle hooks that you can override in your controller to respond to various lifecycle events:

class MyController extends LifecycleController {
  @override
  void onInit() {
    super.onInit();
    // Called when the controller is initialized
  }

  @override
  void onDispose() {
    super.onDispose();
    // Called when the controller is disposed
  }

  @override
  void onDidPush() {
    super.onDidPush();
    // Called when the screen is pushed onto the navigation stack
  }

  @override
  void onDidPop() {
    super.onDidPop();
    // Called when the screen is popped from the navigation stack
  }

  @override
  void onDidPushNext() {
    super.onDidPushNext();
    // Called when a new screen is pushed on top of this one
  }

  @override
  void onDidPopNext() {
    super.onDidPopNext();
    // Called when the screen on top of this one is popped
  }

  @override
  void onResumed() {
    super.onResumed();
    // Called when the app is resumed from the background
  }

  @override
  void onInactive() {
    super.onInactive();
    // Called when the app becomes inactive
  }

  @override
  void onPaused() {
    super.onPaused();
    // Called when the app is paused
  }

  @override
  void onDetached() {
    super.onDetached();
    // Called when the app is detached
  }
}

Asynchronous Operations Handling #

Manage asynchronous tasks with ease using the asyncRun method. It automatically handles loading states and error management.

class DataController extends LifecycleController {
  List<String> _items = [];

  List<String> get items => _items;

  void fetchData() {
    asyncRun(() async {
      // Simulate network request
      await Future.delayed(const Duration(seconds: 2));
      _items = ['Item 1', 'Item 2', 'Item 3'];
      notifyListeners(); // Update UI
    });
  }
}

In your widget, you can show a loading indicator or error message based on the controller's state.

@override
Widget buildView(BuildContext context, DataController controller) {
  if (controller.isLoading) {
    return const Center(child: CircularProgressIndicator());
  } else if (controller.isError) {
    return Center(child: Text('Error: ${controller.errorMessage}'));
  } else {
    return ListView(
      children: controller.items.map((item) => ListTile(title: Text(item))).toList(),
    );
  }
}

Customizing UI Components #

Custom Loading View

Override the buildLoading method in your widget to provide a custom loading UI.

class MyWidget extends LifecycleWidget<MyController> {
  @override
  Widget buildLoading(BuildContext context, MyController controller) {
    return const Center(
      child: CircularProgressIndicator(color: Colors.red),
    );
  }
}

Custom Error View

Override the buildError method to customize the error UI.

class MyWidget extends LifecycleWidget<MyController> {
  @override
  Widget buildError(BuildContext context, MyController controller) {
    return Center(
      child: Text(
        'Oops! ${controller.errorMessage}',
        style: TextStyle(color: Colors.red, fontSize: 18),
      ),
    );
  }
}

Subscription Management #

Manage stream subscriptions efficiently using built-in methods to prevent memory leaks.

Adding Subscriptions

Use the addSubscription method to add a subscription that the controller will manage.

class MyController extends LifecycleController {
  void listenToStream(Stream<int> stream) {
    final subscription = stream.listen((data) {
      // Handle incoming data
    });
    addSubscription(subscription);
  }
}

Cancelling Subscriptions

All added subscriptions are automatically cancelled when the controller is disposed. You can also manually cancel subscriptions if needed.

// Cancel a specific subscription
await cancelSubscription(subscription);

// Cancel all subscriptions
await cancelSubscriptionAll();

Debouncing Actions #

Use the debounce method to prevent a function from being called too frequently.

class SearchController extends LifecycleController {
  void onSearchChanged(String query) {
    debounce(const Duration(milliseconds: 500), () {
      // Perform search
    }, id: 'search');
  }
}

💡 Best Practices #

  1. Keep Controllers Focused: Each controller should manage state for a single screen or feature to maintain clarity and ease of testing.

  2. Use asyncRun for Async Tasks: Utilize asyncRun to handle loading and error states automatically, ensuring consistent UX.

  3. Leverage Lifecycle Hooks: Override lifecycle methods to initialize resources, dispose of them, and respond to navigation events appropriately.

  4. Optimize UI Rebuilds: Use context.select to listen to specific properties, reducing unnecessary widget rebuilds and improving performance.

  5. Handle Errors Gracefully: Always provide user feedback by handling errors using showError and customizing the error UI.

📚 Advanced Usage #

Handling Navigation #

Use lifecycle hooks like onDidPush and onDidPop to navigation-related logic.

class DeepLinkController extends LifecycleController {
  @override
  void onDidPush() {
    super.onDidPush();
    // Check for deep link parameters and handle accordingly
  }
}

Responding to App Lifecycle Changes #

Override app lifecycle methods to pause or resume activities like timers, animations, or network requests.

class VideoController extends LifecycleController {
  @override
  void onPaused() {
    super.onPaused();
    // Pause video playback
  }

  @override
  void onResumed() {
    super.onResumed();
    // Resume video playback
  }
}

Integrating with Other State Management Solutions #

While LifecycleController uses Provider internally, it can be integrated with other state management solutions if needed, providing flexibility.

❓ FAQ #

Q: How does LifecycleController compare to other state management libraries like BLoC or GetX? #

A: LifecycleWidget focuses on providing a simple, provider-based approach to state management with a strong emphasis on lifecycle events. It aims to reduce boilerplate and integrate seamlessly with Flutter's navigation and lifecycle.

Q: Do I need to manually dispose of the controller? #

A: No, the controller is automatically disposed of when the widget is removed from the widget tree.

🤝 Contributing #

Contributions are welcome! If you have suggestions for improvements, new features, or find any issues, please open an issue or submit a pull request on GitHub.

📝 License #

This project is licensed under the MIT License.

0
likes
0
pub points
10%
popularity

Publisher

verified publishersora.fukui.jp

A Flutter library for simplified state and lifecycle management. Offers structured handling of screen events, local state, async operations, and UI updates, promoting clean architecture and maintainability in Flutter apps.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, provider

More

Packages that depend on lifecycle_controller