Skip to content

Multiple Providers

For applications with multiple notifiers, NeatMultiState helps you inject dependencies high up in the widget tree, making them accessible to all descendants.

NeatMultiState

This widget avoids the "pyramid of doom" (nested builders) when you have multiple providers. It supports both independent notifiers and dependent "providers" (like NeatState widgets).

Usage

NeatMultiState(
  // 1. Independent Notifiers
  // Notifiers that don't depend on others.
  independent: [
    (_) => CounterNotifier(),
    (_) => ThemeNotifier(),
  ],

  // 2. Dependent Providers
  // Function builders that can nest other providers or depend on independent ones.
  // These are built in order, so later items can depend on earlier ones.
  providers: [
    (child) => NeatState<UserNotifier, User, void>(
          create: (_) => UserNotifier(),
          builder: (context, user, _) => child,
        ),
    (child) => ProxyProvider0<AuthService>( // Example of interop
          update: (_, __) => AuthService(),
          child: child,
        ),
  ],

  child: MyApp(),
)

Accessing State

To access the state efficiently, use the provided context extensions. See the Reading State page for detailed usage of context.read, context.watch, and context.select.

Example

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

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

// --- Notifiers ---

class ThemeNotifier extends NeatNotifier<ThemeMode, void> {
  ThemeNotifier() : super(ThemeMode.light);

  void toggleTheme() {
    value = value == ThemeMode.light ? ThemeMode.dark : ThemeMode.light;
  }
}

class CounterNotifier extends NeatNotifier<int, String> {
  CounterNotifier() : super(0);

  void increment() {
    value++;
    if (value % 5 == 0) {
      sendAction('Reached $value!');
    }
  }
}

class UserNotifier extends NeatNotifier<User, void> {
  UserNotifier() : super(User(name: 'Guest', age: 25));

  void updateName(String name) {
    value = value.copyWith(name: name);
  }

  void incrementAge() {
    value = value.copyWith(age: value.age + 1);
  }
}

class User {
  final String name;
  final int age;

  User({required this.name, required this.age});

  User copyWith({String? name, int? age}) {
    return User(name: name ?? this.name, age: age ?? this.age);
  }
}

// --- App ---

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

  @override
  Widget build(BuildContext context) {
    return NeatMultiState(
      independent: [
        (_) => CounterNotifier(),
        (_) => UserNotifier(),
        (_) => ThemeNotifier(),
      ],
      child: Builder(
        builder: (context) {
          final themeMode = context.watch<ThemeNotifier>().value;
          return MaterialApp(
            title: 'Neat Multi-State Example',
            debugShowCheckedModeBanner: false,
            themeMode: themeMode,
            theme: ThemeData(useMaterial3: true),
            darkTheme: ThemeData.dark(useMaterial3: true),
            home: const MyHomePage(),
          );
        },
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Neat Multi-State'),
        actions: [
          IconButton(
            icon: context.watch<ThemeNotifier>().value == ThemeMode.light
                ? const Icon(Icons.dark_mode)
                : const Icon(Icons.light_mode),
            onPressed: () => context.read<ThemeNotifier>().toggleTheme(),
          ),
        ],
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            const CounterCard(),
            const SizedBox(height: 16),
            const UserCard(),
          ],
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return NeatState<CounterNotifier, int, String>(
      onAction: (context, action) {
        ScaffoldMessenger.of(
          context,
        ).showSnackBar(SnackBar(content: Text(action)));
      },
      builder: (context, count, _) {
        return Card(
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              children: [
                const Text('Counter Notifier', style: TextStyle(fontSize: 20)),
                const SizedBox(height: 8),
                Text('Count: $count', style: const TextStyle(fontSize: 32)),
                ElevatedButton(
                  onPressed: () => context.read<CounterNotifier>().increment(),
                  child: const Text('Increment'),
                ),
              ],
            ),
          ),
        );
      },
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final name = context.select<UserNotifier, User, String>((u) => u.name);
    final age = context.select<UserNotifier, User, int>((u) => u.age);

    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            const Text('User Notifier', style: TextStyle(fontSize: 20)),
            const SizedBox(height: 8),
            Text('Name: $name', style: const TextStyle(fontSize: 18)),
            Text('Age: $age', style: const TextStyle(fontSize: 18)),
            const SizedBox(height: 16),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: () => context.read<UserNotifier>().updateName(
                    'User ${DateTime.now().second}',
                  ),
                  child: const Text('Update Name'),
                ),
                const SizedBox(width: 8),
                ElevatedButton(
                  onPressed: () => context.read<UserNotifier>().incrementAge(),
                  child: const Text('Increment Age'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

Next Steps