df_pod 0.11.9 copy "df_pod: ^0.11.9" to clipboard
df_pod: ^0.11.9 copied to clipboard

A package offering tools to manage app state using ValueNotifier-like objects called Pods.

example/example.dart

// ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
//
// State Management Example using Pods and GetIt. This example is presented
// in a single file for simplicity, but in a real project, the code would be
// organized across multiple files to maintain clarity and scalability.
// This code can be easily integrated with GoRouter or other routing solutions
// in your Flutter application.
//
// ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓

import 'dart:async';
import 'dart:collection';

import 'package:flutter/material.dart';

import 'package:df_pod/df_pod.dart';
import 'package:get_it/get_it.dart';

// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

// 1. Create the pages for your app, for example:
class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    // 2. Create an "Interpreted Builder" for your page. This is a wrapper
    // that binds the "Page Interpreter" with your page. Continue reading to
    // understand the purpose of the Interpreter.
    return HomePageInterpretedBuilder(
      builder: (context, interpreter) {
        return Column(
          children: [
            // 3. Use the Pods provided by the Interpreter here. Pods are like
            // ValueNotifiers, and PodBuilder functions similarly to a
            // ValueListenableBuilder.
            PodBuilder(
              pod: interpreter.pUserId,
              builder: (context, userIdSnapshot) {
                return Text('UserId: ${userIdSnapshot.value}');
              },
            ),
            // 4. It's generally recommended to avoid placing logic directly
            // within your Page widget, but if necessary, you can use a
            // PodListBuilder like this:
            PodListBuilder(
              podList: [
                interpreter.pConnectionCount,
                interpreter.pNotificationCount,
              ],
              builder: (context, podListSnapshot) {
                final [connectionCount!, notificationCount!] =
                    podListSnapshot.value.toList();
                final notificationRatio = notificationCount / connectionCount;
                return Text('Notification ratio: $notificationRatio');
              },
            ),
            // 5. You way want to consider placing all the logic in the Services
            // or Interpreter instead, to maintain clearer separation between
            // logic and UI code.
            PodBuilder(
              pod: interpreter.pNotificationRatio,
              builder: (context, notificationRatioSnapshot) {
                return Text(
                  'Notification ratio: ${notificationRatioSnapshot.value}',
                );
              },
            ),
            // 6. The above examples show how to bring "Points of Data" (PODS)
            // into the UI. If you need to send updates back to the Services,
            // you can access the Interpreter's Services and invoke their
            // methods:
            OutlinedButton(
              onPressed: () {
                interpreter.authService.login();
              },
              child: const Text('Login'),
            ),
            OutlinedButton(
              onPressed: () {
                interpreter.authService.logout();
              },
              child: const Text('Logout'),
            ),
          ],
        );
      },
    );
  }
}

// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

// 7. Create an Interpreted Builder for your page. This widget passes a specific
// Interpreter to a builder function, simplifying the connection between your
// page and its associated data.
class HomePageInterpretedBuilder extends StatelessWidget {
  final Widget Function(
    BuildContext context,
    HomePageInterpreter interpreter,
  ) builder;

  const HomePageInterpretedBuilder({
    super.key,
    required this.builder,
  });

  @override
  Widget build(BuildContext context) {
    return builder(
      context,
      HomePageInterpreter(
        OtherInterpreter(),
        GetIt.I<AuthService>(),
        GetIt.I<ConnectionService>(),
        GetIt.I<NotificationService>(),
      ),
    );
  }
}

// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

// 8. Create an Interpreter. An Interpreter is a class that interprets Services
// and consolodates/transforms their Pods via mapping or reduction, into simpler
// Pods, that we can directly use in the Page.
class HomePageInterpreter {
  // 9. You can also combine interpreters by passing another interpreter
  // to the constructor.
  final OtherInterpreter otherInterpreter;
  final AuthService authService;
  final ConnectionService connectionService;
  final NotificationService notificationService;

  HomePageInterpreter(
    this.otherInterpreter,
    this.authService,
    this.connectionService,
    this.notificationService,
  );

  // 10. Simplify Pods from Services so the relevant Page can use them
  // without needing to simplify them in the widget code.
  late final pUserId = authService.pUser.map((e) => e!.id);
  late final pNotificationCount =
      notificationService.pNotifications.map((e) => e!.length);
  late final pConnectionCount =
      connectionService.pConnections.map((e) => e!.length);
  late final pNotificationRatio =
      pNotificationCount.reduce(pConnectionCount, (a, b) => a.value / b.value);
  late final pPriorityNotifications = notificationService.pNotifications
      .map((e) => e!.where((e) => e.startsWith('priority:')));

  // 11. Avoid putting anything but Pods such as methods in the Interpreter. The
  // Interpreter is not a Controller. Its sole purpose is to interpret Services
  // and produce Pods for the relevant Page to use.
}

// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

class OtherInterpreter {}

// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

// 12. Define your Services within your app, typically in a directory like
// 'lib/src/services'. Services are responsible for handling the core features
// of your app, such as authentication, push notifications, messaging, API calls,
// and more. The majority of your app's logic should be encapsulated within
// these services. Services manage Pods, which they automatically update
// throughout their lifecycle based on incoming data, state changes, and other
// factors. Interpreters then transform and combine Pods from one or more
// services into simplified Pods that are easy for your pages to use.

// 13. Create a Service for handling authentication.
class AuthService {
  final pUser = Pod<User?>(null);

  // Create a simpler Pod from pUser that indicates whether the user is logged in.
  late final pIsLoggedIn = pUser.map((e) => e != null);

  Future<void> login() async {
    // Simulate logging in.
    await Future.delayed(
      const Duration(seconds: 1),
      () async {
        await pUser.set(const User('123'));

        // Register the connection service when a user logs in.
        GetIt.I.registerLazySingleton<ConnectionService>(
          () => ConnectionService(GetIt.I<AuthService>()),
          dispose: (e) => e.dispose(),
        );
      },
    );
  }

  Future<void> logout() async {
    // Simulate logging out.
    await Future.delayed(
      const Duration(seconds: 1),
      () => pUser.set(null),
    );
    // Unregister the connection service when a user logs out.
    GetIt.I.unregister<ConnectionService>();
  }

  // Dispose all Pods to free up resources.
  void dispose() {
    pUser.dispose();
    // It's not necessary to dispose ChildPods. They will be disposed when
    // their parent Pod is disposed.
    //pIsLoggedIn.dispose();
  }
}

// 13. Imagine this is some kind of social app, we have a service for managing
// the current user's connections.
class ConnectionService {
  final AuthService authService;
  final pConnections = Pod(<User>[]);

  ConnectionService(this.authService);

  void dispose() {
    pConnections.dispose();
  }
}

// Define your data models somewhere in your app, e.g., lib/src/models
class User {
  final String id;
  const User(this.id);
}

// 14. Imagine this app receives notifications from some API, we have a service
// for managing notifications.
class NotificationService {
  StreamSubscription<String>? _streamSubscription;

  final pNotifications = Pod(Queue<String>.from([]));

  NotificationService() {
    _startSteam();
  }

  void _startSteam() {
    _stopStream();
    _streamSubscription = Stream.periodic(const Duration(seconds: 5), (count) {
      return [
        'priority: I love Pods',
        'GetIt is nice',
        'I like Streams.',
      ][count % 3];
    }).listen(_pushMessage);
  }

  void _pushMessage(String message) {
    pNotifications.update((messages) {
      messages.add(message);
      if (messages.length > 100) {
        messages.removeFirst();
      }
      return messages;
    });
  }

  void _stopStream() {
    _streamSubscription?.cancel();
    _streamSubscription = null;
  }

  void dispose() {
    _stopStream();
    pNotifications.dispose();
  }
}

// 15. Register services that should persist throughout the duration of the app.
// This can be called in main.dart.
void onAppStart() {
  GetIt.I
    ..registerLazySingleton<AuthService>(
      () => AuthService(),
      dispose: (e) => e.dispose(),
    )
    ..registerLazySingleton<NotificationService>(
      () => NotificationService(),
      dispose: (e) => e.dispose(),
    );
}
2
likes
160
pub points
57%
popularity

Publisher

verified publisherdevcetra.com

A package offering tools to manage app state using ValueNotifier-like objects called Pods.

Repository (GitHub)
View/report issues

Topics

#bloc #dependency-injection #reactive #riverpod #state-management

Documentation

API reference

Funding

Consider supporting this project:

www.buymeacoffee.com

License

MIT (license)

Dependencies

df_cleanup, df_type, equatable, flutter, meta, shared_preferences, tuple

More

Packages that depend on df_pod