Written by: Tornike Kurdadze | Fri Aug 09 2024 Optimistic State Management in Flutter

Introduction

Optimistic state management is a technique that immediately updates the UI in response to user actions, assuming the operation will succeed. This approach contrasts with traditional methods where the UI waits for server confirmation before reflecting changes. Optimistic updates enhance user experience by making the app feel faster and more responsive.

Key Benefits

  • Improved User Experience: Users receive instant feedback, making the app feel more dynamic.
  • Reduced Perceived Latency: The app appears faster as changes are reflected immediately.
  • Increased User Engagement: Quick responses encourage more interaction.

Implementing Optimistic State Management

Choose a state management solution like Riverpod, or Bloc, etc. Here’s an example using just setState for simplicity.

Step 1: Define the Model

First, create a model class to represent the data. In this case, we’ll create a Post class with a liked status.

@immutable
class Post {
  const Post({
    required this.id,
    this.isLiked = false,
  });

  final int id;
  final bool isLiked;

  Post copyWith({
    int? id,
    bool? isLiked,
  }) {
    return Post(
      id: id ?? this.id,
      isLiked: isLiked ?? this.isLiked,
    );
  }
}
Step 2: Create the UI

We will create a simple stateful widget to display a post and allow users to like it.

class PostItemWidget extends StatefulWidget {
  const PostItemWidget({required this.post, super.key});
  final Post post;

  @override
  State<PostItemWidget> createState() => _PostItemWidgetState();
}

class _PostItemWidgetState extends State<PostItemWidget> {
  late Post _post;

  @override
  void initState() {
    super.initState();
    _post = widget.post;
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.all(24),
      color: Colors.white,
      elevation: 0.5,
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const FlutterLogo(size: 200),
            Row(
              children: [
                Text('Super post #${_post.id}'),
                const Spacer(),
                IconButton(
                  icon: Icon(
                    _post.isLiked ? Icons.favorite : Icons.favorite_border,
                  ),
                  onPressed: _toggleLike,
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
Implement the Like Function

Add a function to toggle the liked status of the post and update the UI before the server responds.

Future<void> _toggleLike() async {
  /// Optimistically update liked status
  setState(() {
    _post = _post.copyWith(isLiked: !_post.isLiked);
  });

  /// Simulate a network call
  final result = await Future<bool>.delayed(const Duration(seconds: 1), () {
    return false;
  });

  /// simulate a network failure
  if (!result && mounted) {
    /// Revert the change if the operation fails
    setState(() {
      _post = _post.copyWith(isLiked: !_post.isLiked);
    });

    /// Show a snackbar to the user
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
        backgroundColor: Colors.red,
        content: Text('Failed to like the post'),
      ),
    );
  }
}

Best Practices

  • Handle Errors Gracefully: Ensure smooth rollbacks and inform the user if an operation fails.
  • Use Caching: Combine optimistic updates with caching for better offline support.
  • Choose Wisely: Apply optimistic updates to actions likely to succeed.
  • Test Extensively: Cover edge cases like network failures and race conditions.

Conclusion

Optimistic state management in Flutter significantly improves app responsiveness and user satisfaction. By carefully implementing this approach, you can create Flutter apps that are both fast and engaging, leading to a superior user experience.

Coffee makes me feel like I have my shit together 😀, so here you go 💙