Publishing an App: a step-by-step guide to the Play Store and App Store
Everything between a finished build and a live listing — signing, provisioning, TestFlight, staged rollout, store assets, data-safety forms, common rejection reasons and private distribution.
Tutorials end at a working build. The distance between that and an app a stranger can install is larger than most plans allow for, and almost none of it is application code. This guide walks through both stores in order, plus the case where an app is for one company and must never appear in search.
Budget roughly a week for a first release, separate from feature work. Account approval, the first signed build and store assets all take longer than expected.
Before either store
- Choose the package name and bundle identifier carefully. Both are permanent. Use reverse-domain form, e.g.
com.company.appname. Changing them later means publishing a different app and losing your users and reviews. - Put the privacy policy on a live URL. Both stores require one, and it must describe what the app and its SDKs actually collect.
- Decide how a reviewer signs in. If the app needs a login, prepare working demo credentials.
- Remove debug leftovers. Test data, verbose logging, staging URLs and hidden developer menus.
Google Play
1. Account
A Google Play developer account is a one-off fee. Personal accounts now go through an identity verification step, and newer personal accounts may face additional testing requirements before public release, so start this early rather than the week you plan to launch.
2. Signing — the decision you cannot undo
Generate an upload keystore and keep it backed up somewhere that is not your laptop. Enrol in Play App Signing so Google holds the final signing key; without it, losing your keystore means you can never update your own app again.
3. Build an app bundle
flutter build appbundle --releaseAn .aab lets Google deliver only the code and resources each device needs, which meaningfully reduces download size compared with a universal APK.
4. Store listing
- Title, short description (80 characters) and full description (4000).
- App icon at 512×512, and a 1024×500 feature graphic.
- Screenshots for phone, and for tablet if you support it.
- Category, contact details and the privacy policy URL.
The short description is what people actually read in search results. Lead with what the app does, not with adjectives.
5. Data safety and content declarations
The Data Safety form must match reality, including data collected by third-party SDKs such as analytics, crash reporting and ads. Getting this wrong is a compliance issue, not a formatting one. You will also complete a content rating questionnaire and declare target audience.
6. Test tracks, then staged rollout
Use internal testing to get the build onto real devices quickly, then release to production as a staged rollout — start at a small percentage, watch crash rates, then increase. A bad build then reaches a fraction of users rather than everyone.
Apple App Store
1. Account
The Apple Developer Program is an annual fee. An organisation account additionally requires a legal entity identifier, which can take days or weeks to obtain. Individual accounts are faster but publish under your personal name.
2. The four things people confuse
- Certificate — proves you are you. Lives in your keychain; expires annually.
- App ID / bundle identifier— the app's permanent unique name.
- Capabilities / entitlements — declared powers such as push notifications, HealthKit or associated domains. They must match between the identifier and the Xcode project, or signing fails or the feature dies silently at runtime.
- Provisioning profile — ties those together and says which devices may run the build.
Nearly every “it built yesterday” failure is one of these four being out of sync, most often after adding a capability.
3. Purpose strings
Every permission needs a usage description in Info.plist, and vague strings get rejected. “Used to attach a photo to your service report” passes; “This app needs camera access” invites a reply.
4. Archive, upload, TestFlight
flutter build ipa --releaseUpload to App Store Connect, then distribute through TestFlight before submitting. Install it on a device that has never had a debug build: missing purpose strings, assets excluded from the release bundle and anything hidden behind a debug flag only surface this way.
5. Submit for review
Review commonly takes around a day, though it varies and first submissions often take longer. Include reviewer notes explaining anything non-obvious, and demo credentials if there is a login.
The most common rejection reasons
- No way for the reviewer to sign in. The most frequent and most avoidable.
- Vague permission strings.
- Digital goods sold outside in-app purchase.Content or features consumed inside the app generally must use the store's payment system; physical goods and real-world services must not.
- Sign-in requirements. If you offer third-party social login, check the current rules on offering an equivalent private option before building the login screen.
- Placeholder content or features that are visibly unfinished.
- Broken links — a privacy policy or support URL that 404s.
- Crashes on the reviewer's device, often an older model or a different region.
- Requesting permissions the app does not need.
- Misleading screenshots showing features that do not exist.
- Inaccurate data declarations.
When rejected, reply in the review system with specifics and a short screen recording. A precise reply usually turns a multi-day loop into one round.
When the app is not for the public
Internal tools break the usual model: they should not be discoverable, and the users are a known workforce rather than customers.
- iOS: distribute as a custom app through Apple Business Manager. It is still reviewed by Apple but is not publicly searchable, and the organisation deploys it to its staff.
- Android: use a private listing through managed Google Play, restricted to the organisation.
Decide this early. It affects account setup, identifiers and who needs to be involved on the client side, and retrofitting it after building for public release is avoidable pain.
Release hygiene
- Version deliberately. Keep the user-facing version separate from the build number, and never reuse a build number.
- Keep the symbols. Without the mapping or dSYM files from that exact build, production crash reports are unreadable.
- Separate environments. Staging and production should use different identifiers so both can sit on one device.
- Automate the repetitive steps — even a script that bumps the build number, builds and uploads removes a class of release-day mistakes.
- Write release notes as you go rather than reconstructing a month of changes on submission day.
None of this is intellectually difficult. All of it is unforgiving about detail, which is why it deserves its own time in the plan rather than the last afternoon of the sprint.
Read next
Flutter State Management: setState, Provider, BLoC and Riverpod compared
A practical guide to choosing state management in Flutter. What each option is good at, where each breaks down…
Flutter Performance: how to find and fix jank, slow lists and memory problems
Why Flutter apps stutter and what to do about it — rebuild scope, list building, image sizing, expensive widge…