atomx 1.0.0 copy "atomx: ^1.0.0" to clipboard
atomx: ^1.0.0 copied to clipboard

A value-centric state management library for Flutter, built on ValueNotifier. Atomx treats values and states as equals, allowing you to attach state directly to values instead of pages.

Atomx #

A value-centric state management library for Flutter, built on ValueNotifier. Atomx treats values and states as equals, allowing you to attach state directly to values instead of pages.

Flutter Dart

âœĻ Features #

ðŸŽŊ Value-Centric 🔄 Reactive ðŸŽĻ Composable ðŸŠķ Lightweight
State belongs to values Automatic UI updates Single builder Native Flutter

🚀 Why Atomx? #

ðŸŽŊ Value-Centric #

State belongs to values, not pages. This means:

  • Data owns its state (loading, error, success)
  • UI simply reflects the data's state
  • Components can be reused anywhere since they're not tied to page state
  • State changes are automatically propagated to all listeners
  • Testing is simpler as you can test state logic independently

For example, the same messages data can be displayed in different ways:

// Chat list Component
class ChatListComponent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AtomxBuilder(
      builder: (context) {
        final messages = GetIt.I<ChatController>().messages;
        return ListView.builder(
          itemCount: messages.length,
          itemBuilder: (context, index) => MessageTile(message: messages[index]),
        );
      },
    );
  }
}

// Chat grid Component
class ChatGridComponent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AtomxBuilder(
      builder: (context) {
        final messages = GetIt.I<ChatController>().messages;
        return GridView.builder(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
          itemCount: messages.length,
          itemBuilder: (context, index) => MessageCard(message: messages[index]),
        );
      },
    );
  }
}

🔄 Reactive #

UI automatically updates when values or states change. This means:

  • No need for manual state propagation
  • UI updates automatically when data changes
  • No need for setState or other manual update triggers

For example, updating a user's profile automatically updates all UI components:

class ProfileController {
  final user = AtomxState<User, UserState>(User.empty(), UserState.initial);
  
  Future<void> updateProfile(String name) async {
    try {
      user.update(state: UserState.loading);
      final updatedUser = await api.updateProfile(name);
      user.update(value: updatedUser, state: UserState.loaded);
    } catch (e) {
      user.update(state: UserState.error);
    }
  }
}

// All these widgets will update automatically when user changes
class ProfileHeader extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AtomxBuilder(
      builder: (context) {
        final user = GetIt.I<ProfileController>().user;
        return Text(user.value.name);
      },
    );
  }
}

class ProfileStats extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AtomxBuilder(
      builder: (context) {
        final user = GetIt.I<ProfileController>().user;
        return Text('${user.value.posts} posts');
      },
    );
  }
}

ðŸŽĻ Composable #

Combine multiple value states in a single builder. This means:

  • No need for multiple builders or listeners
  • Clean and readable code
  • Better performance as all states are handled in one place

For example, handling multiple states in a single builder:

class ProductDetails extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AtomxBuilder(
      builder: (context) {
        final product = GetIt.I<ProductController>().product;
        final reviews = GetIt.I<ProductController>().reviews;
        final related = GetIt.I<ProductController>().related;
        
        // Handle all loading states in one place
        if (product.state == ProductState.loading ||
            reviews.state == ReviewsState.loading ||
            related.state == RelatedState.loading) {
          return LoadingIndicator();
        }
        
        // Handle all error states in one place
        if (product.state == ProductState.error ||
            reviews.state == ReviewsState.error ||
            related.state == RelatedState.error) {
          return ErrorView();
        }
        
        // Build UI with all data
        return Column(
          children: [
            ProductHeader(product: product.value),
            ReviewsList(reviews: reviews.value),
            RelatedProducts(products: related.value),
          ],
        );
      },
    );
  }
}

ðŸŠķ Lightweight #

Built on Flutter's native ValueNotifier. This means:

  • No external dependencies
  • Small bundle size
  • Familiar API for Flutter developers

For example, using native Flutter patterns:

// Familiar ValueNotifier pattern
final counter = Atomx<int>(0);

// Easy to understand state updates
counter.update(value: counter.value + 1);

// Simple state management
final user = AtomxState<User, UserState>(User.empty(), UserState.initial);
user.update(value: newUser, state: UserState.loaded);

ðŸ“Ķ Observable Types #

// 1. Basic value
final counter = Atomx<int>(0);

// 2. Value with state
final user = AtomxState<User, UserState>(User.empty(), UserState.initial);

// 3. List
final todos = AtomxList<Todo>([]);

// 4. Map
final users = AtomxMap<String, User>({});

// 5. List with state
final messages = AtomxListState<Message, MessagesState>([], MessagesState.initial);

// 6. Map with state
final contacts = AtomxMapState<String, Contact, ContactsState>({}, ContactsState.initial);

💎 Example: Chat App #

// Create a controller to manage the chat domain
class ChatController {
  final currentUser = AtomxState<User, UserState>(User.empty(), UserState.initial);
  final messages = AtomxListState<Message, MessagesState>([], MessagesState.initial);
  final contacts = AtomxMapState<String, Contact, BaseState>({}, const LoadingState());

  Future<void> loadCurrentUser() async {
    currentUser.update(state: UserState.loading);
    try {
      final user = await fetchCurrentUser();
      currentUser.update(value: user, state: UserState.loaded);
    } catch (e) {
      currentUser.update(state: UserState.error);
    }
  }
}

// Create a reusable component that reacts to the controller
class MessageList extends StatelessWidget {
  final Function(Message) onMessageSelected;

  const MessageList({
    required this.onMessageSelected,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    final controller = GetIt.I<ChatController>();

    return AtomxBuilder(
      builder: (context) {
        final user = controller.currentUser;
        final messages = controller.messages;
        final contacts = controller.contacts;
        
        if (user.state == UserState.loading || 
            messages.state == MessagesState.loading ||
            contacts.state is LoadingState) {
          return CircularProgressIndicator();
        }

        return ListView.builder(
          itemCount: messages.length,
          itemBuilder: (context, index) {
            final message = messages[index];
            final contact = contacts[message.senderId];
            final isCurrentUser = message.senderId == user.value.id;
            
            return ListTile(
              leading: isCurrentUser ? null : CircleAvatar(
                backgroundImage: NetworkImage(contact?.avatarUrl ?? ''),
              ),
              title: Text(contact?.name ?? 'Unknown'),
              subtitle: Text(message.content),
              trailing: isCurrentUser ? Icon(Icons.check) : null,
              onTap: () => onMessageSelected(message),
            );
          },
        );
      },
    );
  }
}

// Use the component in any page
class ChatPage extends StatelessWidget {
  const ChatPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Chat')),
      body: MessageList(
        onMessageSelected: (message) {
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => MessageDetailsPage(messageId: message.id),
            ),
          );
        },
      ),
    );
  }
}

See the example app for a complete implementation.

0
likes
150
points
36
downloads

Publisher

unverified uploader

Weekly Downloads

A value-centric state management library for Flutter, built on ValueNotifier. Atomx treats values and states as equals, allowing you to attach state directly to values instead of pages.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on atomx