Ideas Engineered for Tomorrow
We Engineer Services & Solutions for Your Business Needs
Consulting Services Hire Book Consulting

Hire Flutter Developers in India

One Dart codebase, two app stores, and a rendering engine you now carry inside your binary. Treat this page as the brief for that hire: the day job as it actually is, a one-hour screen you can run yourself, and the cases where picking Flutter at all would be the mistake.

Jump to Screening

People who set out to hire Flutter developers in India are usually buying a trade, whether or not they have named it. The trade is that you write your product once and accept that everything between your code and the glass belongs to a framework you do not control. Flutter does not ask iOS or Android for a button. It brings its own canvas, paints every pixel itself, and hands the operating system a finished picture. That is the whole reason one team can cover two platforms, and it is also the source of every awkward conversation you will have later.

So the person you are hiring is not simply a Dart programmer. They are someone who has to know where the framework ends. They need to feel when a widget tree is rebuilding far more of itself than it should, know when to stop writing Dart and go write Kotlin, and be honest with you on the day a feature you promised turns out to sit outside what Flutter reaches. Everything below is organised around that boundary: the week-to-week job, the questions that separate a real senior from a fluent mid-level, the limits nobody puts on a pitch deck, and the plain arithmetic of running this work out of India when your product team sits in London, Sydney or San Francisco.

What a Flutter Engineer Does Between Standups

Job adverts list frameworks. Calendars describe jobs. Here is where the hours honestly end up once a Flutter app has real users on it.

Composing widget trees, then arguing with them

Everything visible is a widget, layout included. Padding is a widget. Alignment is a widget. This produces trees that nest deeply and read badly unless somebody keeps splitting them into named classes with meaning. Half of Flutter code review is about that discipline, because a 400-line build method is not a style complaint. It is a performance problem waiting to happen and a merge conflict machine on a team of three.

Deciding where state lives, and then deciding again

Flutter gives you no opinion here. It gives you setState and an InheritedWidget mechanism, and the community filled the gap with Provider, Riverpod, Bloc, GetX, signals and a dozen more. Choosing is the easy part. Living with the choice across forty screens, and knowing when a piece of state has outgrown where it currently sits, is the actual skill.

Writing the native code Flutter did not write for you

Sooner or later Dart runs out. A payments SDK, a hardware peripheral, a background service that has to survive the app being killed, an ID verification library shipped as an AAR and an XCFramework. Then your Flutter engineer is writing Kotlin on one side, Swift on the other, and a channel in the middle, and testing it on both. This work always takes longer than the ticket says.

Keeping the dependency list from rotting

A typical app carries thirty to sixty packages from pub.dev, and their health varies wildly. Some are maintained by the Flutter team. Some were published by one person in 2022 who has since moved on. Watching for abandonment, reading changelogs before upgrading, and keeping the Dart SDK constraint sane is unglamorous work that nobody schedules and everybody eventually pays for.

Chasing frames across two threads

Flutter runs your Dart on one thread and rasterises on another. A stutter caused by too much building looks identical to the user as a stutter caused by expensive painting, and the fixes share nothing. Reading the DevTools timeline to tell those two apart is a daily skill, and the engineers who cannot do it guess instead, which is how apps acquire a layer of RepaintBoundary widgets that achieve nothing.

Running two releases that have to agree with each other

The same Dart ships to Google Play and the App Store, but the two get there by different routes with different rules, different review behaviour and different rollback controls. Somebody has to hold both, keep version numbering coherent, and know which store is the bottleneck this week. On small teams that somebody is the Flutter developer, whether or not the job description mentioned it.

When Is Flutter the Right Call, and When Is It Not?

Answer this before you hire, not after. Reversing the decision two quarters in is expensive in a way that shows up on a roadmap rather than an invoice.

Where Flutter is clearly the correct answer

You want both platforms, you have one budget, and your interface is yours rather than the platform's. Flutter draws every pixel, so a designer who has invented a brand language gets it rendered identically on an iPhone 13 and a mid-range Android without two engineers interpreting the same Figma file differently. That last point is undersold. The bug class it removes is not crashes, it is the endless drip of tickets saying the spacing is wrong on Android and the sheet animates differently on iOS.

It also wins when the app is broad rather than deep. A field service tool with sixty screens, forms, offline sync and a barcode scanner is mostly the same code twice if you build it natively. One team writing that once, with a shared model layer and shared validation, will out-deliver two smaller native teams on the same money almost every time. Not because Flutter is magic, but because you stopped paying for the same feature twice.

Where native is still the right answer

Reach for native when the operating system is the product. Anything that lives in the home screen, the lock screen, the watch, the car or the system share sheet is not a Flutter problem. Home screen widgets on iOS must be written in SwiftUI. Live Activities are native. Apple Watch and Wear OS apps are native. CarPlay and Android Auto are native. You can ship a Flutter app with a native widget beside it, and plenty of teams do, but then you are hiring both skills and should plan for that from the start.

Reach for native, too, when your app is essentially a thin wrapper around one heavy platform capability. A camera app whose value is capture quality, a health app built on HealthKit's newest additions, an app whose entire feature is a background audio pipeline. In each case you would spend your Flutter engineer's time writing platform channels to reach something a native engineer would already have in hand.

The third case is the one people ignore: a codebase that already exists and works. Rewriting a stable native app into Flutter to save future maintenance is a bet that takes years to pay back and usually loses the small behaviours that made the app feel finished. Add Flutter to it as a module for new screens if you want, that path is supported. Do not put the working app in the bin.

Where React Native genuinely beats it

Say this out loud during scoping, because a vendor who cannot is not advising you. If your company already runs on TypeScript and React, if there is a web product sharing business logic with the app, and if you would rather hire from the enormous JavaScript pool than the much smaller Dart one, React Native is the better organisational fit even where Flutter is the better technical one. Hiring is a constraint like any other.

Also retire the bridge argument if you hear it. React Native's newer architecture replaced the old asynchronous bridge with a direct JavaScript interface, so pitching Flutter on the grounds that the competition has a slow bridge is a talking point from several years ago. Anyone still using it as their main argument has stopped reading. Compare on drawing model, ecosystem depth for your specific integrations, and who you can actually hire. If you are weighing it seriously, our React Native developers in India page covers the other side of the same decision.

Flutter beyond mobile, with the asterisk

Web, macOS, Windows and Linux are all supported targets, and the desktop ones are a genuinely good deal for internal tools where you control distribution. Web is the one to be careful with. Flutter web paints into a canvas, which means your text is not ordinary DOM text. Selection, browser find, SEO and screen reader behaviour all work differently from a normal site, and for a public marketing surface that is usually disqualifying. Flutter web is a reasonable choice for a signed-in application behind a login. It is a poor choice for anything Google needs to index.

Dart Is the Part Buyers Underestimate

Every other decision on this page has a technical answer. This one is about supply, and it quietly shapes how long your search takes and who you end up talking to.

Nobody arrives as a Dart developer

Dart exists almost entirely to serve Flutter. There is no large body of Dart backends, no Dart data science community, no wave of graduates who learned it first. That makes it structurally different from JavaScript, where a React Native team can recruit from the web, or from Swift and Kotlin, where the platform itself creates a steady supply. Every Flutter engineer you meet converted from somewhere else, on purpose, usually within the last few years.

The practical consequence is that the pool is thinner than the noise around Flutter suggests, and it thins dramatically at the top. Screening therefore matters more here than on a React or Java hire, because there are fewer candidates and a higher proportion of them learned the framework from tutorials rather than from a shipped product with a crash rate.

The maximum plausible experience is about seven years

Flutter 1.0 shipped in December 2018. Sound null safety landed with Dart 2.12 in March 2021, and Flutter 3 brought the desktop targets to stable in May 2022. Do that subtraction and the ceiling on genuine production Flutter experience is around seven years as of today, and anyone claiming ten is claiming something that does not exist.

Those dates are also a useful probe. Ask what a candidate remembers about migrating a codebase to null safety. Someone who lived through it will describe the mixed-mode period, the packages that lagged, and the moment they finally deleted the last exclamation mark. Someone who arrived after 2022 has never seen a non-nullable Dart type as anything other than the default, which is fine, but you now know their frame of reference.

What their previous ecosystem predicts about their gaps

This is the single most useful question in a Flutter screen and it takes ten seconds: what were you doing before Flutter? The answer forecasts the shape of their blind spots with uncomfortable accuracy.

Converted from Android

Comfortable with Gradle, manifests, signing keys, background execution limits and Play Console. Usually the fastest to unblock a build failure. The gap is iOS: provisioning profiles, App Store Connect roles, and why the Apple side of a plugin behaves differently. Ask what they have personally submitted to the App Store.

Converted from iOS

Strong instincts about release discipline and about animation quality, because both were expensive where they came from. The gap is Android's fragmentation: device-specific behaviour, aggressive OEM battery managers killing background work, and the sheer range of screen densities. Ask which Android devices they own.

Converted from web

Often the cleanest Dart and the best sense of component structure, because they had already learned it in React or Vue. The gap is everything under the framework. They may never have opened Xcode, signed anything, or debugged a native crash. On a small team with no native support around them, this is the risky profile.

None of these is a rejection. They are instructions for where to push in the interview and where the person will need cover in their first month. What you cannot afford is to discover the gap in week six, when a Gradle upgrade breaks the release build and the only person on the project has never seen one.

What this does to your search

Two things. First, widen your definition: a strong Android or iOS engineer with a year of solid Flutter will usually beat a three-year Flutter engineer who has only ever built tutorials, because the platform knowledge underneath is what you are short of. Second, expect the search itself to take longer than a React hire, which is exactly why the timing section further down this page matters. The candidate you want probably has a job right now.

The Flutter Stack We Hire For

No one candidate is deep in all of it. What matters is whether their depth lines up with the parts your product leans on, and whether they will admit which items here they have only ever read about.

Dart Flutter 3.x Impeller Riverpod Provider Bloc / Cubit ValueNotifier Freezed build_runner go_router Dio Retrofit for Dart Drift Isar / Hive Platform Channels Pigeon dart:ffi Isolates flutter_test integration_test Golden tests Patrol mocktail DevTools Firebase Supabase Sentry Fastlane Codemagic FVM Melos Rive

How Do You Screen a Flutter Developer in an Hour?

Six topics, ten minutes apiece, not one algorithm puzzle among them. Every topic maps to something that costs money after release. Keep the order, because what you hear early tells you where to lean harder later.

Rebuild scope, and why const is not a style rule

Start here, because it is the fastest way to find out whether someone understands the framework or just uses it. Ask what actually happens when setState is called. The answer you want is that the enclosing State is marked dirty and its entire build method runs again on the next frame, producing a fresh widget tree that Flutter then compares against the existing element tree.

Then push: if the build method returns a widget that is identical to the one already there, what does the framework do? The good answer names the short circuit. A const constructor produces a canonicalised instance, so the same const widget in two consecutive builds is literally the same object in memory, and the framework can skip updating that entire subtree rather than walking it. That is why const is not cosmetic. It is the difference between rebuilding a leaf and rebuilding a branch, sixty times a second.

The follow-up separates people cleanly. Ask what they would do about a screen where a small timer widget updates every second and the whole page rebuilds with it. Weak answers reach for a state management package. Strong answers move the setState down: wrap only the changing part in its own StatefulWidget, or a ValueListenableBuilder, so the rebuild boundary sits around the thing that actually changes. Rebuild scope is a structural decision, and someone who reaches for a library before restructuring the tree will do that to your codebase everywhere.

BuildContext, and the four ways people get it wrong

Ask what a BuildContext actually is. The answer is that it is a handle on this widget's position in the element tree, which is how lookups walk upward to find the nearest Theme, Navigator, MediaQuery or provider. It is not a global, and it is not interchangeable.

Then ask for the mistakes they have personally made. There are four common ones and a good candidate will volunteer at least two. Using a context across an asynchronous gap, so a Navigator call fires after the widget has already been disposed and you get an exception in production that never reproduces locally. Calling an inherited widget lookup inside initState, before the element is positioned in a tree where the lookup can succeed. Using the parent's context in a builder when the thing being looked up was introduced by the widget itself, which is what Builder exists to solve and what causes the classic Scaffold lookup failure. And holding a context on a long-lived object, which quietly keeps a disposed subtree alive.

The mounted check after an await is the one to insist on. If a candidate does not mention guarding on mounted before touching context following asynchronous work, they have not maintained an app where users navigate away mid-request, which is to say they have not maintained an app.

State management, and what each choice signals

There is no correct answer. There are informative ones. Ask what they used last, why, and what they would not use again.

setState only

Correct far more often than the internet admits, especially for local interaction state like whether a panel is expanded. A candidate who says they still use it for local state is showing judgement. A candidate who uses it for shared application state across screens is going to hand you a prop-drilling problem.

Provider

A thin, sensible wrapper over InheritedWidget with ChangeNotifier. Signals someone who learned Flutter in its middle period and values simplicity. The weakness they should be able to name is that lookups are resolved at runtime by type, so a missing provider becomes an exception when the screen opens rather than an error when you build.

Riverpod

Providers declared outside the widget tree, resolved without needing a BuildContext, and checkable by the compiler and its code generator. Signals a current engineer who cares about testability. Ask about auto-dispose and about family providers, because those are where people who only read the introduction come unstuck.

Bloc or Cubit

Events in, states out, everything explicit and traceable. Verbose for a small app and genuinely useful on a large one with several engineers, because the pattern is enforced rather than agreed. Signals someone from a bigger team or a regulated product. Ask when Cubit is enough, because insisting on full Bloc everywhere is over-engineering.

GetX deserves its own note. It bundles state, routing, dependency injection and more behind a very short syntax, and it is popular with engineers optimising for how fast they can write a screen. In a codebase you intend to keep for years it tends to make testing harder and hide where things come from. It is not a disqualification, but a candidate who reaches for it by default and cannot articulate the trade-off is telling you what they optimise for, and it probably is not the thing you are optimising for.

The strongest signal of all is when a candidate says the choice matters less than consistency, and then describes what they do when they inherit a codebase using something they dislike. Extending it is the mature answer. Proposing a migration in week one is not.

Platform channels, and the moment Dart runs out

Every serious Flutter app eventually needs something the framework does not expose. Ask a candidate to walk you through the last time they wrote a platform channel themselves rather than installing a package for it.

The mechanics you want them to know: a MethodChannel for request and response calls, an EventChannel for a stream coming from the native side such as sensor readings or Bluetooth state, matching channel names on both sides, and arguments restricted to types the standard codec can carry. They should also know Pigeon exists, which generates the Dart, Kotlin and Swift interfaces from one schema so that a typo in a channel name becomes a compile error instead of a runtime null. On a project of any size, hand-written channels are a false economy and Pigeon is the better default.

Two deeper probes. Ask what they do when the native library they need is C rather than Java or Objective-C, and listen for dart:ffi rather than a channel. Ask what happens when platform code has to call Dart back on a background isolate, because the plugin messenger is bound to the root isolate by default and that trips up almost everyone the first time. Someone who has hit that has done real integration work.

Isolates, and the myth that async makes things parallel

This one exposes a widespread misunderstanding, so ask it even of senior candidates. Dart runs your code on a single thread with an event loop. An await does not create parallelism. It yields so other queued work can run, and then resumes.

So ask directly: you parse a twelve megabyte JSON response and the UI freezes for a second. Why does making the function async not fix it? The answer is that the parse is synchronous CPU work on the UI isolate and no amount of await moves it off. The fix is another isolate, which has its own memory and its own event loop and communicates by message passing rather than shared state.

Then get specific. Isolate.run for a one-shot job is the modern shape, compute is the older helper doing much the same thing, and a long-lived isolate with ports is what you want when the work is repeated rather than occasional, because spawning is not free. Ask what can and cannot be sent across the boundary and listen for the fact that messages are copied, which means throwing a huge object across can cost as much as the work you were trying to move. Ask whether a plugin can be called from a spawned isolate and whether they have had to arrange the messenger for it. Candidates who have only read about isolates give a clean textbook answer and stop; candidates who have used them mention the copying cost unprompted, because it bit them.

The janky list, which is really a diagnosis question

Describe a symptom and refuse to describe a cause. A product list judders when you flick it fast, gets noticeably worse on a two-year-old Android, and looks perfectly fine in debug mode on the engineer's own phone. What do you check?

First, listen for them to reject the premise. Debug builds are not indicative because Dart is running from a just-in-time compiler; performance work happens in profile mode on a physical device or it does not count. A candidate who does not say that has never profiled properly.

Then the diagnosis. The right instinct is to open the DevTools performance timeline and find out which thread is over budget, because the UI thread and the raster thread mean two different investigations. Slow frames on the UI thread point at build and layout: rebuild scope too wide, an unbounded ListView with a children list instead of the builder constructor, expensive work happening inside a builder callback, images being decoded at full resolution when they are displayed at a fraction of it, or a heavy computation sitting in build where it runs on every frame. Slow frames on the raster thread point at painting: large blurs, saveLayer calls from opacity or clipping applied to complex subtrees, shadows over big areas, and shader compilation stalls on first run of an animation.

In my experience the answer is rebuild scope roughly two times out of three, and an image pipeline problem most of the rest. But the ratio is not the point. The point is whether they measure, change one thing, measure again, and can tell you which number moved. Ask that last question explicitly. It is the difference between an engineer and someone who has memorised a listicle of optimisations.

The Limits That Do Not Appear in the Pitch Deck

Anyone selling you Flutter will describe the upside accurately. Here is the other half, which you need before you sign, not in month four.

Flutter paints its own widgets, so it is always slightly behind the OS

Because Flutter draws everything itself rather than asking the platform for controls, it has to reimplement whatever the platform does. When Apple or Google ships a new design language, new system controls or a new look for the whole interface, native apps often pick up a good part of it by recompiling. Flutter apps get it when the Flutter team implements it and you upgrade. That lag is usually months and occasionally longer.

For most products this is irrelevant. For a consumer app whose users expect it to look like the rest of their phone the week a new OS lands, it is a real consideration and worth asking your engineer about directly. The related point is that Flutter's Cupertino widgets are an imitation of Apple's controls, not the controls themselves, so they lag on both appearance and behaviour in small ways that reviewers occasionally notice.

Accessibility runs through a translation layer

Screen readers do not see your widgets. They see a semantics tree that Flutter builds and hands to the platform accessibility APIs. Most of the time this works and Flutter fills it in automatically. When it does not, the fix is annotating the tree with Semantics widgets yourself, and finding out that it does not requires actually turning on TalkBack and VoiceOver rather than trusting a linter. If you have an accessibility obligation, put it in the brief and test it on both platforms with the readers switched on, because a Flutter app that has never been driven by a screen reader will have gaps.

What Flutter does not reach at all

Home screen widgets, Live Activities and complications are native code, always. Watch apps are native. CarPlay and Android Auto are native. App Clips and Instant Apps are native. Deep system integrations such as custom keyboards, share extensions and Siri or Assistant intents are native, though some have partial packages. None of this stops you shipping a Flutter app. It means those features are separate work in a separate language by someone who knows that platform, and if any of them are on your roadmap they need to be costed now rather than discovered later.

Embedded platform views are the other soft spot. A native map, a web view or a camera preview is composited into the Flutter scene rather than drawn by it, and that composition has a cost that shows up as jank when there are several of them, or when one sits inside a scrolling list. If your design puts three maps on a page, raise it in the first week.

What a plugin gap actually costs you mid-project

This is the failure that hurts most, because it arrives after commitments have been made. The pattern is consistent. A feature is planned assuming a package exists on pub.dev. It does exist. It was last published two years ago, supports an older Dart SDK constraint, has fourteen open issues and does not implement the one method you need.

From there you have three options and all of them are engineering work. Fork it and maintain your own copy, which is fastest and means you now own a native codebase on two platforms forever. Patch it through a dependency override, which is fine for a small fix and awkward when it drifts. Or write your own channel against the vendor's native SDK, which is the honest answer for anything on your critical path such as payments, identity or DRM. Reckon in weeks rather than days, and note that the work needs a person comfortable in Kotlin and Swift, which loops back to the conversion question earlier on this page.

The way to avoid it is to check before you commit. For anything load-bearing, look at when the package was last published, whether the publisher is verified, how many open issues sit unanswered, whether it supports every platform you target rather than just Android, and whether the API covers your actual use case or only the demo. Ten minutes of that during scoping prevents the conversation where a date slips for a reason nobody can explain to a board.

App size, measured rather than argued about

A Flutter app is bigger than the same app built natively, and the reason is structural: the rendering engine and the Dart runtime ship inside your binary instead of living in the operating system. This matters if your users are on cheap devices with little storage or paying for data by the megabyte, and it matters not at all for an enterprise tool installed once through an MDM.

Do not accept a number from anyone, this page included, because it moves with every release and with your own assets and fonts. Get your engineer to run the size analysis on a real release build. Flutter ships tooling for this: build with the size analysis flag and open the result in DevTools, which breaks the binary down by package and asset so you can see whether the problem is the engine or the four full icon fonts somebody added. Play's app bundle format already splits by architecture and density for you. Deferred components can push rarely used features out of the initial download, though that mechanism is Android only, which is itself worth knowing.

You still need native knowledge within reach

Perhaps the most useful thing on this page. Flutter reduces how much native work you do; it does not remove the need for someone who can do it. Gradle will fail with an error message written for Android engineers. Signing will need setting up on both stores. A crash will land in Sentry with a native stack trace. A store rejection will cite a rule that has nothing to do with Dart. An SDK will ship as an AAR with its own manifest requirements.

You do not necessarily need a full-time iOS or Android engineer. You need the knowledge available, either in the Flutter engineer you hire, which is why their previous ecosystem matters so much, or through someone you can call. If you are covering that gap deliberately, our iOS developers in India page describes the native side of the same app.

Testing a Flutter App Without Buying Yourself a Maintenance Problem

Flutter's test tooling is better than most cross-platform stacks and worse than its reputation in one specific place. Know which is which before you set a coverage target.

Widget tests are the ones that pay

A widget test runs in a headless environment with a fake frame scheduler, pumps your widget, finds things in the tree and asserts on them. There is no device and no emulator, so a suite of several hundred runs in seconds, which is what makes it usable in a pull request check.

The detail that separates people who write them from people who talk about them is the difference between pumping a single frame and settling. Pumping advances one frame. Settling repeats until no frames are scheduled, which never terminates if you have an indefinite animation such as a spinner on screen, so the test hangs and someone eventually deletes it. Ask a candidate about that and about how they test a widget whose data arrives asynchronously. Faking the clock and controlling the future is the answer. Adding a real delay and hoping is not.

Golden tests, and why they go flaky

A golden test renders a widget and compares it pixel by pixel against a stored reference image. When it works it is the cheapest visual regression protection you will ever have, and it is genuinely valuable on a design system where a padding change three components deep would otherwise reach production unseen.

It goes wrong for a boring reason. Rendering is not identical across machines, operating systems and Flutter versions. Fonts, anti-aliasing and text shaping all differ enough to move pixels, so goldens generated on an engineer's Mac fail on a Linux CI runner, and someone regenerates them with the update flag until the test asserts nothing at all. The discipline that fixes it: generate goldens only on one pinned CI image, never locally, pin the Flutter version with something like FVM so upgrades are deliberate, load real fonts in the test setup rather than relying on the default test font, and keep goldens for components rather than whole screens so a diff points at something specific. A candidate who has maintained goldens will tell you all this unprompted, usually with visible feeling.

integration_test for the flows that actually matter

The integration_test package replaced the older driver approach and runs your test inside the app on a real device or emulator, which means plugins, platform channels and real network behaviour are all in play. It is slow, and it belongs on a small number of flows that would be catastrophic to break: sign-in, checkout, the offline sync path, whatever your business calls the money path.

Where it gets awkward is anything outside the Flutter view, such as a system permission dialog or a native payment sheet, because your test cannot tap what Flutter did not draw. Patrol is the common answer, wrapping native automation so those dialogs can be handled. It is worth knowing the tool exists; it is also worth deciding whether that flow is better covered by a human before release.

What to ask, and what to ignore

Ask what they test and what they deliberately do not. A good answer is opinionated: business logic and state notifiers under unit test, component behaviour and edge states under widget test, the design system under goldens, three or four critical journeys under integration_test, and no attempt to reach a coverage percentage for its own sake. Ask what they do about flaky tests, and the answer you want is that a test which fails randomly gets fixed or deleted, because a suite people ignore is worse than no suite. If test discipline is a gap on your side rather than theirs, our mobile testing services in India cover the device matrix and release QA separately from the build team.

Shipping to Both Stores at Once Is Two Review Processes, Not One

This is where cross-platform stops saving you time. The code is shared. The distribution is not, and nobody warns you about the second half.

The two processes have almost nothing in common

Google Play and the App Store differ in how you upload, how you test, how review works, how you roll out and how you stop a bad release. Play uses an app bundle and a set of testing tracks running from internal through closed and open to production. Apple uses TestFlight for pre-release distribution and a review queue in front of every public build. Play's review is often quick and sometimes not; Apple's involves a person applying written guidelines who can refuse you for reasons that have nothing to do with your code. Both change their rules through the year, and Play in particular enforces a target API level requirement on a schedule that will eventually force an upgrade whether or not you had planned one.

What that means for your hire is simple. The person running your releases needs both accounts, both mental models, and enough experience to know which store is the constraint on any given release. Ask a candidate to describe the last release they ran end to end on both platforms in the same week. The answer is either detailed or absent.

One Dart bug becomes two rollbacks

Here is the consequence of shared code that people miss. A logic error in Dart is in both apps simultaneously, so an incident is never contained to one platform. And the two platforms give you different tools to stop it. Play lets you halt a staged rollout partway through. Apple lets you pause a phased release. Neither removes the app from a phone that already updated. There is no rollback in the sense a backend engineer means the word.

Two practices follow. Stage on both sides rather than releasing to everyone, and make sure the person watching the crash dashboards is awake in the hours after each rollout begins, which is a timezone question as much as a process one. And put a remote configuration flag in front of anything genuinely risky, so a payment change or a new sync path can be switched off from a server without waiting on either store. Ask candidates whether they have shipped behind flags. The ones who have will describe the incident that taught them.

The macOS machine, and other build logistics

Building for iOS requires macOS, which is a practical constraint on any distributed team. Either you run hosted macOS runners on a service such as Codemagic, Bitrise or GitHub Actions, or somebody owns a Mac that becomes a single point of failure. Decide which, early, and make sure signing credentials live in the pipeline rather than on a laptop.

The rest is version hygiene that sounds trivial until it bites. Flutter's version field maps to both a user-visible name and a build number, and the two stores treat those differently, so a mismatch means a rejected upload at the worst possible moment. Pin the Flutter SDK version per project so a developer upgrading locally cannot silently change what CI produces. Automate the upload with Fastlane on both sides so the release is a script rather than a person remembering the order of nine steps.

The paperwork nobody budgets for

Both stores now require declarations about what data your app collects and why. Apple asks for privacy details on the listing and requires privacy manifests from SDKs you include. Google asks you to complete a data safety form. The awkward part is that the answers depend on packages somebody added months ago, so the person filling in the form has to actually audit the dependency list rather than guess.

Treat those declarations as engineering work with a person's name on it. Getting them wrong is a compliance problem rather than a bug, and what any of it means for your obligations under GDPR or whichever privacy law applies to you belongs with your own counsel rather than with a development vendor. What we can do is tell you accurately what the app collects and which package collects it.

The Flutter Seniority Ladder, and What Each Level Can Be Left With

Titles mean different things at different companies. What somebody can be trusted with unsupervised does not drift like that. Match your situation to a rung below: aiming too high wastes money, and aiming too low costs you a quarter.

Junior: builds screens, does not touch the stores

Builds a screen somebody else designed, against an API that already exists, writes the widget test for it, sticks to whatever patterns the repository already uses, and closes a bug that was described properly. Anything touching state architecture, platform channels or local persistence wants a reviewer on it. Do not let them be the only person who can cut a release; that is a statement about your exposure, not about their ability. A good hire where a senior sits on the same codebase and there is specified work queued up.

Mid-level: owns features, needs cover on architecture

Takes a loose description to a shipped feature including empty states, error handling, offline behaviour and tests. Can open DevTools when pointed at a slow screen. Understands rebuild scope well enough not to create obvious waste, and knows when a piece of state should move. Usually still discovering where the store processes bite. Pound for pound the best value on a team where somebody else owns direction.

Senior: owns the architecture and both releases

Picks the state management approach and can defend it against the alternatives. Writes the platform channels rather than waiting for a package. Holds signing on both stores, runs the pipeline, reads crash reports down to a root cause without being handed a reproduction, and pushes back on any change that ships with no remote off switch. This is the level you need when there is no other mobile engineer on your side. Anything less and you are the architect, whether you wanted to be or not.

Lead: owns how the team works, not only the code

Settles where the module boundaries fall, what your test strategy is actually protecting, and the bar a review has to clear. Owns the design system approach and keeps the goldens trustworthy enough that people believe them. Makes the native boundary a deliberate line rather than an accumulation of accidents, and mentors so quality is not one person's attention span. Worth it at three or more Flutter engineers, or once a monorepo of several packages has become the thing slowing delivery down. Under that size a good senior covers the same ground with far less process.

One warning about counting years. Flutter has moved quickly, and somebody with five years may have spent four of them on a single app that never adopted null safety properly, never used isolates and never shipped a platform channel. Ask which Flutter version their last shipped release ran on, and what they actually built in the past year and a half. That tells you far more than the length of the title.

Four Situations That Bring People to This Page

These are patterns we see repeatedly, assembled from the shape of the requests rather than from any one client. Find the one nearest yours and the brief writes itself.

Situation one: two native apps that have drifted apart

You have an iOS app and an Android app built by different people at different times. They are supposed to be the same product. They are not. The Android one is two features behind, the checkout flows differ in ways nobody documented, and every product decision now costs two implementations and two arguments about what the design meant.

What actually goes wrong if you rush this: a full rewrite into Flutter is announced, six months are booked, and month five arrives with ninety percent of the features and none of the small behaviours users depended on. The version people liked is gone and the reviews say so.

The saner brief is incremental. Flutter supports being added to an existing app as a module, so you build the new shared screens in Flutter, embed them in both native shells, and converge over several releases rather than one. You need a senior who has done add-to-app integration specifically, because the routing and state handoff between the native shell and the Flutter view is where this approach gets messy. Ask that question in the first interview.

Situation two: the app the agency built, then stopped answering emails about

A Flutter app exists and works. The people who wrote it are gone. Nobody on your side has opened the repository. Now something needs changing and you discover the Flutter version is two years old, half the packages will not resolve against a current SDK, the signing keys are in an email thread, and the state management is a pattern that person invented.

The first job is not features. It is getting a build out of the existing code on a current toolchain, which usually means walking the Flutter version forward one step at a time rather than jumping to latest, resolving each round of package breakage as it appears. Then the keys and store accounts get moved into your organisation. Then somebody writes down how a release is produced. Only after that does the roadmap restart.

The person for this is a senior who is comfortable reading someone else's architecture without immediately proposing a rewrite. It is a temperament as much as a skill, and it is worth screening for by handing them a genuinely odd piece of code and watching whether they get curious or contemptuous.

Situation three: the prototype that got funded

Someone built a convincing Flutter demo, it did its job, and now it has to become a product. Everything is in one folder, network calls sit inside build methods, there are no tests, secrets are in the repository, and it has never been on a device other than the founder's phone.

The temptation is to start again. Usually wrong, because the prototype encodes product decisions that took months to reach and would be re-litigated from scratch. The right first month is structural: split the code into layers, put the API behind a client with real error handling, choose a state approach and apply it consistently rather than everywhere at once, add widget tests around the flows you are about to change, get CI producing signed builds for both stores, and rotate every credential that was ever committed.

This suits a strong senior working alone for the first stretch, then a second engineer once the structure holds. Adding three people to a prototype in week one produces conflicts, not speed.

Situation four: the app that is embarrassing to scroll

The features are all there. The app feels cheap. Lists stutter, transitions drop frames, the splash sits too long before anything appears, and someone senior has started sending screen recordings with the word unacceptable attached.

This is a measurement job before it is a coding job, and it is the one most commonly mis-scoped as a rewrite. Profile mode on the oldest device your analytics say matters. Find the slow frames. Establish which thread. Then work the list: rebuild scope, list construction, image decode resolution, expensive work in build, opacity and clipping over big areas, and shader compilation on first animation. Fix one at a time and record the frame timings before and after.

The right brief here is a scoped engagement with a measured target rather than a headcount, because you can tell when it is done. Bring the ninety-ninth percentile frame time under budget on the named device set, and stop.

How Flutter Delivery From India Actually Runs

Reassurance is not an answer to the timezone question. Arithmetic is, so here it is. India Standard Time is fixed at UTC+5:30 and nothing here shifts for daylight saving in either direction. Your clocks move twice a year; ours never do, so the distance between us changes without anybody in Mumbai touching a thing. Check the numbers below rather than taking them on trust. It is only addition.

The overlap, worked out

A standard day in our offices is 09:30 to 18:30 IST. Take five and a half hours off the top and it becomes 04:00 through 13:00 UTC. Now hold that up beside a 09:00 to 17:00 working day wherever you happen to sit.

Where you are Hours you and Mumbai genuinely share Their 09:30 to 18:30 IST, translated
Dubai (UTC+4)Effectively all of it08:00 to 17:00
Singapore (UTC+8)Five, from midday onward12:00 to 21:00
Amsterdam or Berlin, summer (UTC+2)Six, once you arrive06:00 to 15:00
London, winter (UTC+0)Four, running out at 13:0004:00 to 13:00
London, summer (UTC+1)Five, running out at 14:0005:00 to 14:00
Sydney, standard time (UTC+10)Three, starting 14:0014:00 to 23:00
Auckland, standard time (UTC+12)One, starting 16:0016:00 to 01:00
New York, summer (UTC-4)None. They finish as you sit down.00:00 to 09:00
New York, winter (UTC-5)None, and an hour short of touching.23:00 to 08:00
Chicago, summer (UTC-5)None, and an hour short of touching.23:00 to 08:00
San Francisco, summer (UTC-7)None, missing you by three hours.21:00 to 06:00

Read that honestly. The Gulf, Europe, the UK and Singapore get a shared working window at no cost. Australia and New Zealand get a narrow one at the tail of their day. Both American coasts get nothing whatsoever on a standard Indian schedule, and anyone who tells you otherwise is quietly redefining business hours.

What a US window really costs

It is reachable. It is also a rota decision rather than a favour. Sharing four hours with New York in summer means the Indian day sitting at roughly 13:30 until 22:30 IST, which produces a common 09:00 to 13:00 on the Eastern clock. San Francisco needs it pushed further back, near 15:30 until 00:30 IST, for three hours between 09:00 and 12:00 Pacific. Both are evening and overnight shifts for whoever works them. That shapes who accepts the role, how long they stay, and how the engagement feels in month four once the novelty has gone.

We would rather name that shift up front, put the window in the engagement, and hold it steady instead of quietly cycling people through it. Round-the-clock cover is not free either. Passing work between shifts needs several sets of people, handover notes written every single day, and a coordination load most products this size cannot pay for. If you want that, we will price it as the thing it is.

The release timing problem the gap creates

Here is a specifically mobile consequence that generic offshore advice misses. Store notifications arrive when they arrive. An App Review rejection or a Play policy flag can land at three in the morning in one country and mid-afternoon in the other, and a staged rollout you started at the end of the Indian day has its first crash data appearing while everyone here is asleep.

The fix is procedural and it takes one conversation. Decide who reads the store notifications first. Decide which replies can be sent without a meeting, such as clarifying a test account or explaining an existing feature, and which need a product decision from your side. Decide who watches the crash dashboard for the first hours of a rollout and what threshold makes them halt it. One conversation deletes a whole class of wasted days. Skip it and every round trip costs a working day; three rounds and a release has eaten a week for reasons that have nothing to do with code.

The device set, which matters more on Flutter than people expect

Flutter's promise is that it looks the same everywhere, which makes it tempting to test on one phone. That promise covers your drawing, not the platform underneath it. Keyboard and text input behaviour, permission dialogs, background execution on aggressive Android OEM builds, camera plugins on specific chipsets and web view rendering all vary by device and by manufacturer. We build the test handset list out of your own install-base data instead of whatever launched recently, and it always includes a cheap Android two or three years old, because that is where your slowest frames live.

Working practices that survive the distance

Decisions land in writing the same day

Whatever a call settles gets typed into the ticket before that day ends. This is not paperwork for its own sake. Someone who was asleep while you talked has no other route to acting on the outcome, short of booking another call, and a year later it is the only record of why the architecture went the way it did.

Pull requests small enough to review

Opened against your repository under your rules, with a description that explains the why rather than restating the diff, because a reviewer eight hours away cannot lean over and ask. Large branches landing on a Friday are the most reliable way to make remote code review stop working, and Flutter makes them easy to create because widget diffs are noisy.

Both stores in your organisation's name

Your Apple Developer Program membership, your Google Play developer account, your repository, with engineers added as users holding defined roles. Access is granted rather than transferred at the end. There is then nothing to hand back, because it was always yours.

Done includes a build somebody installed

Merging the pull request does not close the ticket. It closes once the build sits on TestFlight and an internal Play track, is installed on the target handsets, and has its frame timings and crash rate held against the previous version. Settle that early and the argument never arrives.

English judged on writing, not accent

At this distance what matters is whether an engineer can put an unambiguous question in writing and keep a ticket update clear enough to act on. That is what gets assessed. Somebody who talks well but writes vaguely damages a remote project far more than the opposite ever does.

One named person you deal with

You talk to the engineer doing the work, not through a layer that relays. If the engagement is large enough to need coordination, that is a named role with a defined job rather than a translator between you and the people writing Dart.

Everything here that is not Flutter-specific, including contracting, IP assignment and how an engagement is governed, sits on the hire developers in India hub. Terms covering notice, handover and data handling get settled with you in the master agreement before work begins, not inferred from something you read online.

Engagement Models for Flutter Work

The right shape turns on two things: whether a mobile engineer already sits on your side, and whether this work has an end date. Commercial terms get settled with you during scoping rather than published as a rate card.

Embedded engineer

Flutter engineers who sit inside your sprint board, your standup and your review rules, and who adopt the conventions already in your repository rather than bringing ours along. Fits a roadmap that keeps going, with somebody of yours steering it. Your engineering lead deals with them, and there is no account layer in the middle.

Scoped build

One outcome, with finished written down before anyone starts: a first release onto both stores, an add-to-app module for an existing native pair, a prototype turned into something maintainable, or a performance piece with a measured frame target. Suits anything where you can describe the end state.

Keeping a live app alive

For an app that ships rarely but still needs the Flutter version moved forward, packages kept current, the annual Play target API requirement met, crashes triaged and releases put out on both stores. This is the request that reaches us most often, and it is the least expensive way to keep an app from quietly rotting between funded projects.

Why this reaches a working engineer sooner than recruiting does

Companies rarely come to us because Flutter capability cannot be found. They come because of the calendar. Do the recruiting yourself, here or anywhere else, and you get the same sequence every time: find people, screen them, run a technical round, make an offer, and then wait. The waiting is what catches overseas employers out, because whatever notice the candidate owes the employer they already have is contractual, frequently longer than you would guess, and none of it is yours to shorten. Put that question to every candidate early, before a start date goes anywhere near a plan. Somebody you choose in March can very reasonably still be unavailable in June.

If your problem is a store deadline this quarter, or a scroll that is losing you reviews right now, June is not an answer. The reason we can move differently is not clever process. It is only that the search does not have to start when you call, because the engineers are already employed here and between projects, owing a leaving date to nobody. That is what makes a shortlist reach you within 48 hours of your brief and puts whoever you pick on your codebase within 7 days.

None of that speed replaces your own judgement. Interview them anyway. Put the rebuild-scope and isolate questions from earlier on this page in front of them, and turn the person down when the fit is not there. A shortlist that arrives quickly only helps if it is worth reading in the first place.

Frequently Asked Questions

Is Flutter fast enough, or will we regret the decision in a year?

Release builds compile Dart ahead of time to native machine code, so raw execution speed is rarely what hurts. What hurts is rebuild scope: a setState too high in the tree that repaints half the screen sixty times a second. That is a code problem with a known fix, not a framework ceiling. Teams that regret Flutter usually regret a plugin gap or a platform feature, not the frame rate.

How do you tell a senior Flutter developer from a confident mid-level one?

Ask them to describe a jank they diagnosed and which thread it was on. The UI thread and the raster thread fail for different reasons and the fixes have nothing in common. A mid-level engineer names optimisations. A senior one describes how they narrowed it down in DevTools, what number moved, and which change they reverted because it made no difference.

What happens when a plugin we depend on stops being maintained?

You fork it, patch it through a dependency override, and from that day you own two native implementations plus a Dart interface. Budget it as engineering work, because it is. The way to reduce the risk is to check publish dates and open issue counts on pub.dev before you adopt anything that sits on the critical path, rather than after your build stops compiling.

Do we still need iOS and Android engineers if we go Flutter?

Not full time, usually, but you need access to that knowledge. Someone has to read a Gradle failure, set up signing on both stores, write the Swift or Kotlin half of a platform channel, and answer a store rejection that has nothing to do with Dart. A Flutter engineer who converted from native carries most of that. One who converted from web frequently does not.

Can a Flutter developer in India work with our US team?

Not on a normal Indian schedule. IST holds at UTC+5:30 all year, so a 09:30 to 18:30 day here maps to 04:00 until 13:00 UTC and is finished before New York switches on. Genuine US overlap means an evening shift on this side. That is workable and it is a staffing choice, so it belongs in the engagement before anybody starts.

Should we choose Flutter or React Native?

Count your JavaScript engineers first. If you have several and a web product sharing logic with the app, React Native keeps one hiring pool and one type system. If your app is heavy on custom drawing, motion or brand-exact interfaces, Flutter gives you control that is harder to reach on the other side. The old bridge argument is dated and should not decide it.

How big will our app be if we build it in Flutter?

Larger than the equivalent native app, because the rendering engine ships inside the binary rather than living in the operating system. Nobody should quote you a figure, including this page, since it moves with every release and with your own assets. Run flutter build apk --analyze-size and the equivalent iOS size report on a real build and argue from your numbers.

Tell us what state your Flutter app is in

A first build, two native apps you want to converge, a prototype that got funded, or a scroll that stutters on cheap Android. Describe where you actually are and we will tell you what seniority that calls for, and whether Flutter deserves to be the answer at all.