PullingManager: Simple Real-Time Updates for Flutter
Building real-time apps in Flutter? You’ve probably wrestled with how to handle data updates. After a client requested a flexible polling solution for their app, I developed PullingManager, a package that makes data updates simple and battery-friendly.
Why I Built PullingManager
Every Flutter developer knows the pain of managing real-time updates. You want your app to stay current but must also consider battery life, network usage, and application state. PullingManager emerged from these exact challenges in production apps.
What’s Under the Hood
PullingManager is built on RxDart, and for good reason. The reactive approach gives us some powerful capabilities:
First, we use exhaustMap
to handle overlapping requests. If your app is fetching data and another request comes in, exhaustMap
Ensures we finish the first request before starting the next. This prevents the all-too-common problem of response conflicts.
The switchMap
operator lets us change polling frequencies on the fly. Think about a trading app - you might want 1-second updates on the trading screen but slower updates on the portfolio overview. PullingManager makes this kind of dynamic adjustment trivial.
Real-World Example: EV Charging Station Monitor
Let me share a real implementation we used in an electric vehicle charging station monitoring app. This example shows how PullingManager adapts to different app states and user contexts:
final pullManager = PullingManager(
fetchData: () => chargingRepository.getChargingState(),
initialFrequency: PollingFrequency.low,
immediateFirstFetch: true,
attachToLifecycle: true,
lowFrequencyDuration: const Duration(seconds: 10),
mediumFrequencyDuration: const Duration(seconds: 5),
highFrequencyDuration: const Duration(seconds: 1),
);
And then listen to the pulling calls:
pullManager.dataStream.listen((data) {
/// do something with the data
}
Context-Aware Polling: The frequency automatically adjusts based on which screen the user is on. We use low-frequency updates (every 10 seconds) on the home screen but switch to high-frequency updates (every second) when actively charging or connecting.
currentRouteSubject
.listen(
(currentRoute) {
pullManager.setFrequency(
currentRoute.when(
home: () => PollingFrequency.low, // Slower updates on home screen
activeCharging: () => PollingFrequency.high, // Fast updates during charging
connectCharger: () => PollingFrequency.medium, // Fast updates during connection
pairing: () => PollingFrequency.high, // Fast updates on pairing screen
),
),
)
.addTo(compositeSubscription);
- Clean Data Handling: The data stream provides a structured way to handle updates, errors, and status changes.
The Battery Problem (and How We Solved It)
One of our early users reported a significant battery drain when running polling in the background. The solution? We added lifecycle awareness. Set attachToLifecycle
to true, and PullingManager automatically pauses when your app goes to the background and resumes when it's active again. Simple, but effective.
Pulling vs Socket: Real Talk
Here’s where we need to be honest — pulling isn’t always the answer. Here’s what I’ve learned from implementing both approaches:
Pulling shines in apps that need periodic updates — think weather apps or social feeds. It’s simpler to implement and easier on battery life. I use it for most of my projects where sub-second updates aren’t critical.
Sockets are the way to go for true real-time needs. If you’re building a chat app or a live collaboration tool, the immediate updates from sockets are worth the additional complexity and resource usage.
Getting Started
PullingManager is now open source, and I’d love to see what you build with it. The API is straightforward:
- Pick your polling frequency (low, medium, or high)
- Decide if you want lifecycle awareness
- Start pulling
What’s Next?
I’m actively maintaining this project and would love your input. Whether you’ve found a bug, want to contribute a feature, or just have suggestions, head over to the GitHub repo and let me know.
Remember — choose pulling for simplicity and efficiency, sockets for immediacy. There’s no one-size-fits-all solution, but understanding these tradeoffs will help you make the right choice for your app.
Try it out!
https://pub.dev/packages/pulling_manager