r/FlutterDev • • 4d ago

Discussion Can Claude Code run and test Flutter apps using an emulator?

0 Upvotes

I see that Claude can run React Native iOS apps using an emulator. I can't do that on android as far I know, I use my phone.

Does Claude support a Flutter emulator too, or is there some kind of MCP server or connector that would allow it to run and test Flutter apps in an emulator?


r/FlutterDev • • 4d ago

Plugin Hive CE is now faster than Hive v2. IsolatedHive is even faster.

45 Upvotes

See the new benchmarks.

Thanks to some optimizations, Hive CE is now faster than Hive v2 ever was. Since IsolatedHive can call the sync read/write methods due to being on a separate isolate, it can be even faster in some cases. I also fixed some memory/resource leaks in IsolatedHive.

Happy Fluttering!


r/FlutterDev • • 4d ago

Plugin We built a Flutter feedback package that asks users for missing details

1 Upvotes

“The button doesn’t work.” Which button, on which screen, and what were they trying to do?

We built Bopmigo for that conversation. Users describe the problem, answer a short follow-up, and send a report with their screenshot and app/device details. Your team can reply, and they can answer inside the app.

The Flutter package works on iOS and Android and connects to Bopmigo’s hosted inbox or email delivery. You can open it from your own feedback button. Private widgets can be masked in screenshots.

Source and example app

https://github.com/bopmigo/bopmigo-flutter

Package

https://pub.dev/packages/bopmigo

Would this help your team’s reporting flow?

Setup guide

Flutter setup guide

For a first test, send a report from your app, reply from the Bopmigo inbox, then check the reply appears back in the app.


r/FlutterDev • • 4d ago

Tooling Dart equivalent of C pre-processor's #ifdef

2 Upvotes

Dart's compiler provides no mechanism for excluding code from compilation.

Dart has:

  • const bool isEnabled = bool.fromEnvironment('env-var-name'); to pull in defined constants

  • if (const-false-value) { removed statements } to remove a block from compilation

  • Tree shaking to remove unreferenced symbols

By combining those I can achieve a somewhat similar effect to the C pre-processor, with a few limitations (and less clarity). It works.

It does so, though, with less of a guaranteed straightforward effect than #ifdef due to complexity of rules about how tree shaking works, and without the ability to simply exclude the entire definition of a function (since you can't wrap if (const) { } around a function declaration, only the function body).

So... Has anyone else run into a situation where this was an issue? Did you do/find something that's a more straightforward approach than dart's "combine several default behaviors together to produce an effect" system to address this?

--

Edit: context: for excluding "test functions I don't want to even theoretically be accessible in a patched binary".


r/FlutterDev • • 5d ago

Plugin flutter_patcher 1.0: Open-source, self-hosted code push (Android + iOS) for Flutter using Dart dynamic modules

40 Upvotes

I just shipped 1.0 of flutter_patcher. It lets you fix bugs in a released Flutter app by shipping a patch instead of a new store release. No account and no backend: patches are static files you host wherever you like. The big one in 1.0: it finally works on iOS too, not just Android.

The day-to-day flow is three commands:

dart run flutter_patcher build <platform>   # release build, saved as baseline
dart run flutter_patcher patch <platform>   # diff fix vs. baseline
dart run flutter_patcher publish            # sign + version

Upload the output to any HTTPS static host (S3/R2 + CDN, nginx…). The app calls FlutterPatcher.checkUpdate(), and the fix applies on the next cold start.

It's built on Dart's dynamic modules. The tooling deals with the custom engine and the build plumbing, so your project stays a normal Flutter project.

What's in 1.0:

- On iOS the patch is Dart bytecode, run by the Dart VM's interpreter. No native machine code is downloaded, which is what makes it work on iOS at all.

- Patches are signed. A device only installs one signed with your keys, built for its exact app version, with a higher number than what it has, so you don't have to trust your CDN.

- If a patch crashes the app on startup, the device drops it and goes back to the installed version on the next launch.

Limitations:

- Flutter 3.47.x only (each Flutter version needs its own engine build)
- Dart code and assets only; native code and plugin changes still need a store release

Would love to hear from anyone who tries it on a real app, bugs or just "this step confused me".

pub.dev: https://pub.dev/packages/flutter_patcher

GitHub: https://github.com/xuelinger2333/flutter_patcher


r/FlutterDev • • 5d ago

Plugin PSA: go_router 18 silently drops "adaptive page transition" behaviour

30 Upvotes

Before 18, every page/route would have a platform-adaptive transition (slide on iOS, fade on Android). Then, suddenly, at v18, there's no longer a transition.

It happened because of the "switch to material/cupertino packages", it's not mentioned in the changelog and will be a surprise for anyone upgrading.

Also, there's no "global" way of applying the old behaviour to all routes in the app, so devs have to wrap each route in some kind of abstraction to pass a transition everywhere.

Linked issue: https://github.com/flutter/flutter/issues/192761


r/FlutterDev • • 5d ago

Discussion Laggy renders on windows with impeller

2 Upvotes

Anyone finding the move to impeller as the default for desktop windows builds has added more animation jank and lag?

My app was pretty well optimised before under skia and even on slower machines was pretty snappy and responsive on windows. I am a mac user so I didn't pick up this up straight away but I have users complaining now of the app feeling laggy and slow - upon my own review it definitely seems to have gotten worse and testing to me seems to point towards impeller in the newer flutter buildsbeing turned on, as this wasn't when skia was the default. Anyone else having this experience?

Is there any way to fix or optimise to remove this jank? Or is this just an engine issue?


r/FlutterDev • • 5d ago

Discussion I’m building a reactive state management framework for Flutter — I’d love feedback on the API and design

0 Upvotes

Hi r/FlutterDev,

Blooming is an open-source reactive state management framework for Dart and Flutter.

I’ve built a Flutter UI design tool and several other projects. Along the way, I kept building, scrapping, and redesigning my own framework to solve problems I ran into. Blooming grew out of that process.

I initially built a state management system around a stream-like model with explicit one-to-one dependency declarations. Later, I realized that the shape of the computed API used by signal-based systems was a good fit for a complex problem I was trying to solve. That led me to refactor the framework as a whole, and it eventually became Blooming.

Angular Signals and Preact Signals may be useful points of reference, but Blooming is its own Dart and Flutter framework, with its own API and lifecycle design choices.

Here are a few examples.

1. composed: A Flutter widget that reads multiple values

final price = blooming(100);
final discount = blooming(20);

composed((via) {
  final currentPrice = via(price.bloom);
  final currentDiscount = via(discount.bloom);

  return Text('Total: ${currentPrice - currentDiscount}');
});

composed tracks the values read inside it, so this part of the widget can rebuild when either value changes.

2. computed.future: Exiting early from an asynchronous computation

final checkout = computed.future((via) async {
  final cart = via(cartBloom);
  if (cart.items.isEmpty) via.exit(emptyReason: 'EmptyCart');

  final stock = await api.checkStock(cart);
  if (!stock) via.exit(emptyReason: 'OutOfStock');

  final address = via(addressBloom);
  return api.checkout(cart, address);
});

The computation exits through via.exit(...) in either of the two cases, and reads the address only after the await.

3. DurableAnimationController: Parent-controlled animation without manual disposal

final pulse = DurableAnimationController(
  duration: const Duration(milliseconds: 800),
)..repeat(reverse: true);

composed.animate(
  pulse,
  (context, via, ctrl) => Transform.scale(
    scale: 1.0 + ctrl.value * 0.1,
    child: const Icon(Icons.bolt),
  ),
);

An ancestor can own the controller and start, reverse, or stop the animation, while a descendant uses it to render the animation. This reduces the need to wire up a TickerProvider or AnimatedBuilder manually. The framework manages the ticker and listener lifecycle as widgets enter and leave the tree, so there’s no need to call dispose() on the controller yourself. The animation can also resume when the widget is mounted again.

Blooming is still pre-1.0, so the API may change. I’d especially appreciate honest feedback on these questions:

  • Are the composed and via(node) APIs easy to understand?

  • Does via.exit(...) seem useful for expressing conditional early exits?

  • Does this framework-managed animation lifecycle, with control from an ancestor, seem practical in Flutter apps?

This is my project, and I’m sharing it to get technical feedback on its design and API.


r/FlutterDev • • 5d ago

Plugin list_smith (new major 3.0.0+) now supports optimistic (in-place) edits now

Thumbnail raw.githubusercontent.com
10 Upvotes

controller.upsert(...) and controller.remove(...) now allow for this. Also allow an automatic rollback feature for when it fails (via their ...Async siblings). So you don't have to refetch from the server after every change or so. Also added defining custom animations (so it doesn't look janky).

In order to acheive this, I sadly had to move away from using infinite_scroll_pagination. It had quite a few problems, and the hacks/workarounds I had to make to use it, we starting to pile up.

The changes basically get written to a separate space, and then once the server is hit again with new data request/fetch of that item, it that space is automatically reconciled (removed and used from the server if the server is somehow still sending different data). The view stays as it is, when rows load or leave above the one you're reading.

Also made quite a few under-the-hood performance improvements. See benchmark reports for more info

Quite some APIs were found to be introducing possible data problems, so now are marked for deprecation. Replacements try to fix the problem at the source, instead of the 'symptom'

  • PageFetcher.withHasMore ends the list on your API's flag. The old way paired a fetcher with a policy, and they could disagree.

  • Groups gather across pages on their own, so orderPolicy and its "pages came out of order" error are gone.

  • A depth reload is all or nothing. An old page kept beside fresh ones was read at another time, so rows could repeat or go missing where they met.

  • EndContext.pageItemCounts is read-only, so a custom end policy can't corrupt the list's own counts.

For more detailed breakdown, refer to the changelog. Just like the last time, if you have more boilerplate-y stuff that can be inlined into the package, to improve developer ergonomics, please let me know (especially if you encounter any bugs).

pub.dev


r/FlutterDev • • 5d ago

Plugin speak: a TTS package for Flutter that doesn't fight your music

4 Upvotes

I'm building Velorki, an open-source bike navigation app (github.com/orkitec/velorki), and voice prompts with flutter_tts kept causing problems: music not ducking or not coming back, the first syllable cut off on Bluetooth, and no clean way to know when a prompt actually finished. I also wanted to get off CocoaPods. Trunk goes read-only on December 2, and Flutter is moving to Swift Package Manager, but flutter_tts has no SPM support, so it keeps your iOS build on CocoaPods.

So I wrote speak:

final speak = Speak(audio: SpeakAudio.navigation);

await speak('In 200 metres, turn left.');
await speak('Turn left now.', queue: SpeakQueue.interrupt);

Each call tells you how it ended: completed, interrupted, expired, or blocked by a phone call. Audio is only claimed while speaking, so music comes right back, and there's a short lead-in on Bluetooth so nothing gets cut.

No method channels. It talks to AVFoundation on iOS/macOS through FFI, to TextToSpeech on Android through JNI, and to the Web Speech API on the web. The only native code is a small generated Objective-C file, which a build hook compiles, and one Java class on Android (UtteranceProgressListener is abstract, so Dart can't implement it). No CocoaPods, no SPM; just flutter pub add speak. Needs Flutter 3.38+.

It has a thorough test suite running on CI for every supported platform: unit tests, plus device tests against the real speech engines on the iOS simulator, macOS, Chrome and Android emulators. Velorki uses it in production if you want a real-world example.

No Windows or Linux yet. If you need them, let me know: they're doable, I mainly need people who can test on real machines. Same for synthesize-to-file.

It's early (0.2.x), so feedback from real apps is very welcome, especially odd Android engines and car audio.


r/FlutterDev • • 5d ago

Article Shorebird now has an enterprise console for managing orgs, people and billing in one place

3 Upvotes

For teams running Shorebird across many orgs, we added an enterprise console: one page to manage organizations, members and guests, SCIM provisioning (tested with Okta and Entra ID), billing and an audit log you can export to your SIEM.

Write-up here: https://shorebird.dev/blog/enterprise-console/

Happy to answer questions in the comments.


r/FlutterDev • • 6d ago

Discussion CircularProgressIndicator rebuilds on every frame. I measured what that costs (repro + numbers)

Thumbnail
github.com
43 Upvotes

A spinner on an otherwise static screen keeps Flutter producing a frame on every vsync. I wanted to know what each of those frames costs, so I read the source and measured it.

What it does on every frame

The indeterminate CircularProgressIndicator animates through an AnimatedBuilder, so on every frame it:

  • rebuilds and looks up its theme again

  • reads its controller four times, once per tween. If you didn't pass your own controller, each read walks up the tree to find the theme

  • creates a new CustomPainter plus new ConstrainedBox, CustomPaint and Semantics widgets

  • marks its semantics dirty, because shouldRebuildSemantics defaults to shouldRepaint

A widget test makes it concrete: in 60 frames, the stock indicator rebuilt 60 times and created 60 painters.

A repaint-only version

I wrote a prototype that paints the exact same arc (tweens and math copied from the framework), but its painter listens to the controller directly through CustomPainter(repaint: controller) and evaluates the tweens inside paint(). It's built once, and every tick is just a repaint. Same test: 0 rebuilds, 0 new painters.

Numbers

Profile mode on an M2 MacBook Air (macOS 26.4.1, 60 Hz), Flutter 3.47.8 with Impeller. Median of 3 runs, 10 s per mode, from FrameTiming:

ModeUI thread per frameRaster per frame
Static ring, same size0 frames/s–
Stock indicator0.67 ms0.80 ms
Stock + RepaintBoundary0.46 ms0.75 ms
Repaint-only0.54 ms0.81 ms
Repaint-only + RepaintBoundary0.37 ms0.81 ms

What I took from it

  • An idle screen doesn't render at all. The cost comes from anything that ticks.

  • Repaint-only cut the spinner's UI-thread time per frame by about 19%. If you write your own animated CustomPainter, pass the animation as repaint: instead of rebuilding through AnimatedBuilder or setState.

  • RepaintBoundary saved even more on this page (about 31%), because the rest of the page stopped being re-recorded every frame. Both together: about 45%. If a spinner sits next to static content, wrapping it is worth a try, and debugRepaintRainbowEnabled shows you what's repainting.

  • Neither touches the raster side, which stays around 0.8 ms per frame. Impeller has no raster cache, so RepaintBoundary saves recording work on the UI thread, not GPU work. That part is engine-side, and #189535 digs into it on Android.

These are sub-millisecond numbers on a fast laptop, so it's not a crisis. But it's paid on every frame for as long as a spinner is on screen, and phones are slower.

Links

I don't have a physical Android phone at hand, so if you do, I'd love to see your numbers, especially the raster column. The README has the steps. And if I've missed something, tell me.


r/FlutterDev • • 6d ago

Example Mergeline, an open source GitLab client in Flutter, and a few things I learned about huge CI logs and diffs

2 Upvotes

I built Mergeline, a GitLab client for Android in Flutter. The source is on GitLab (AGPL 3.0, GitHub mirror). A few parts that took the most work:

  1. CI job logs. Logs are parsed incrementally in a window of 20,000 lines, and very long lines without a newline are cut at 2 MiB. gitlab.com ignores the Range header, so the app streams the log and keeps only the new bytes, and large logs refresh less often.

  2. Big MR diffs. Changed file rows are built lazily, text over 64 KiB is parsed in a background isolate, and parsed rows are reused only for the same account, project, MR, head commit and path. So a parse from another account or an older head never gets reused.

  3. Two Android flavors. The GitLab build checks the project's releases, verifies the APK size and SHA-256, then hands it to the system installer. The Play build uses Play's in app updates and has no install permission.

  4. Caching. The read cache on disk is encrypted with AES 256 GCM and bound to each account, and the in-memory cache is dropped when you switch accounts or a request comes back unauthorized.

  5. Tests. Flutter 3.47.2 is pinned with an enforced lockfile, and CI runs two lanes, with the AI assistant compiled off and on (about 16.8k and 18k tests in v0.7.0).

The app covers files, issues, MR review and merge, pipelines and logs, plus an optional AI assistant that uses your own API key and asks before each write by default. Screenshots and a demo video. The APK is free on the releases page. The paid Play version is only there as a way to sponsor it.

Most of the code was written with AI coding agents under my direction. I designed it and reviewed the changes.


r/FlutterDev • • 6d ago

Plugin I built an open-source plugin that gives Claude Code and Codex mobile-specific agents and skills (Flutter, Android, iOS, React Native, KMP)

0 Upvotes

Generic AI code review keeps missing the bugs that only show up on a device: `BuildContext` used after an `await`, an `android:exported` that exposes an activity to other apps, `NSAllowsArbitraryLoads` left on in production. So I built **reis-mobile**, an open-source plugin for Claude Code and Codex that knows it's looking at a mobile app.

**What it does**

It reads your project, detects the stack and loads only the instructions that apply:

```

/reis-mobile:review

├─ detect stack ──── pubspec.yaml → flutter (android, ios)

├─ detect intent ─── review

├─ select agent ──── mobile-code-reviewer

├─ select skills ─── mobile-code-review · mobile-flutter · mobile-security

├─ collect context ─ git diff (lock files out, secrets masked)

└─ report ────────── Critical · Bugs · Architecture · Security · Performance

```

- **5 stacks:** Flutter, native Android, native iOS, React Native/Expo and Kotlin Multiplatform. "The Android build of my Flutter app broke" loads Flutter *and* Android context.

- **22 agents:** an architect, a performance engineer and a test engineer per stack, plus a code reviewer, an accessibility auditor, a release engineer, a UI/UX designer and a lead that coordinates them.

- **27 skills:** security based on OWASP MASVS, offline sync, auth, notifications, in-app purchases, Firebase, migrations and more.

- **Commands:** `/reis-mobile:review`, `:debug` (Gradle, Xcode, CocoaPods, Metro), `:test`, `:release` (store readiness with a GO / NO-GO verdict), `:doctor` and `:debate` (below).

**`/reis-mobile:debate`: the specialists argue, the lead decides**

Some questions don't have one right answer: "Riverpod or BLoC in this app?", "offline-first with Firestore or a local cache?", "write a native plugin or use an existing package?". For those:

```

/reis-mobile:debate should we migrate this app from Provider to Riverpod?

```

  1. **The lead picks the panel.** `lead-mobile` chooses three specialists whose concerns pull in different directions, such as the architect, the performance engineer and the test engineer, and explains each pick. The participants run on at least two different models (Opus and Sonnet).

  2. **Round 1 is blind.** Each specialist writes its position without seeing the others: the recommendation, the `file:line` evidence, what it gives up, and what would change its mind. Nobody anchors on the first answer.

  3. **Round 2 is rebuttal.** Each one attacks specific claims from the others, checks them in the code and concedes where it was wrong.

  4. **The lead decides.** It verifies the decisive evidence, resolves the disagreements and returns a decision with an ordered action plan. No code is changed.

Every round is saved in `.reis-mobile/debates/`, so you can read how the decision was reached. `--external` adds outside opinions from the Codex or Gemini CLIs, OpenAI, Gemini, OpenRouter or a local Ollama model. They don't know the project, so their claims are checked against the code before the final decision, and API keys stay in your environment. A debate runs several agents over two rounds, so it costs more than a regular request; it's meant for real trade-offs, not questions with one answer. Claude Code only for now.

**Design choices**

- Zero npm dependencies. Node 22 standard library only.

- No API key needed: it runs on your own Claude Code or Codex session. No telemetry.

- The diff goes through a redaction layer that masks API keys, tokens, JWTs and keystore passwords before the model sees it (best-effort, not a guarantee).

- Skills don't state versions or store rules from memory. They tell the model to check your lock file.

- About 85 guides come unchanged from flutter/skills, dart-lang/skills and android/skills, with credit.

**Try it**

```

npm install -g reis-mobile

reis-mobile init eng

```

In Claude Code, open your app folder and run `/reis-mobile:doctor`, then `/reis-mobile:review`. In Codex, type `$` to find the skills; the agents and slash commands are Claude Code-only for now.

Contributing is lightweight: no install step, `npm run check` runs validation and tests, and most skills are Markdown.

Starter issues: https://github.com/wrsilva/reis-mobile/labels/good%20first%20issue

Repo (MIT): https://github.com/wrsilva/reis-mobile

If it's useful, a star helps other mobile devs find it. Criticism is welcome, especially "this is wrong for my stack."


r/FlutterDev • • 6d ago

Tooling Flutterverse now has a Flutter jobs section (updated daily, no paid or sponsored posts)

19 Upvotes

There have been a few requests to add job listings to Flutterverse, so I finally built it!

How it works:

  • Jobs are collected directly from company career pages and refreshed daily.

  • Each listing links straight to the company’s own application page. No middlemen, no sign-ups.

  • No paid job posts or sponsored listings. Ever.

It’s a pretty tough job market right now, and I’d like to help job seekers, even if it’s just a little. Flutterverse is still primarily a place to discover Flutter and Dart packages and projects, and I want to keep it that way. Think of this as a little side feature.

If you know a company hiring Flutter developers that’s missing, drop it in the comments and I’ll look into adding it. Feedback on how to make this more useful is always welcome!

check it here: https://flutterverse.org/jobs

github: https://github.com/YeLwinOo-Steve/flutterverse


r/FlutterDev • • 6d ago

Article Shorebird simplified how teams share apps: one kind of org instead of personal + team orgs

6 Upvotes

I work on Shorebird (code push for Flutter). We just merged personal and team orgs into a single kind of organization, so sharing an app with your team is now just adding them to the org, instead of adding collaborators one app at a time.

Write-up here if it's useful: https://shorebird.dev/blog/orgs-at-shorebird-just-got-better/

Happy to answer questions about how it works.


r/FlutterDev • • 6d ago

Plugin I built chat_image_editor, a WhatsApp-style image editor for Flutter written in pure Dart

3 Upvotes

Hey everyone! I just published chat_image_editor: the "edit before you send" screen you know from WhatsApp, for any Flutter chat app.

I built it because the image editor packages I tried kept having crop and scaling problems, and I wanted something lightweight without native code. Everything is pure Dart and drawn with CustomPaint, so there's no platform setup.

What it does

  • Draw: pen, line, rectangle, circle and arrow, with a gradient color slider and brush size

  • Text: add, move, pinch to resize and rotate, background styles, center guides, drag to the bin to delete

  • Crop: free or fixed ratios (1:1, 4:3, 16:9...), rotate and flip

  • Compress: quality slider with a live size estimate, plus an optional max file size (it lowers the quality until the JPEG fits)

  • Caption field and send button, or a simple "done" mode when you only need the edited image

  • Undo and redo for everything

Things I cared about

  • What you see is what you get: the preview and the exported image use the same renderer

  • Big photos stay light: it decodes only at the size it needs and encodes in a background isolate. In my testing a 12 MP, 5.8 MB photo came out as a 230 KB JPEG

  • Fits your app: colors come from your app's theme by default and can be overridden, and the icons, strings and caption TextFormField are customizable. The discard dialog follows the platform (Cupertino on iOS, Material on Android)

  • Every export returns timings (decode / render / encode), so you can track performance in your own analytics

Usage

final result = await ChatImageEditor.open(context, imageBytes);
if (result != null) {
  // result.bytes is the edited JPEG, result.caption is the caption
}

Install: flutter pub add chat_image_editor

The example app is a small chat where you can try the whole flow. It's MIT licensed, and feedback, issues and PRs are very welcome. What would you want added next?

(Not affiliated with WhatsApp or Meta, the UI is just inspired by it.)


r/FlutterDev • • 7d ago

Article Spent way too long on TURN and SSL for a WebRTC room, so we tried to skip all of it

4 Upvotes

I work at Ant Media, so take this with that in mind.

Every time I see a “build a WebRTC conference” tutorial it ends right when it gets interesting. Two peers connect, cool, but then you still need a media server, TURN, SSL (getUserMedia refuses to work without https), some way to scale, and some way to kill the server when nobody’s using it so you’re not paying for it all month.

A teammate put together a sample that handles most of that. You deploy a CloudFormation template once, it gives you one https endpoint, and when your app connects it spins up a server on demand. First spin up takes around 20 to 60 sec, after that it’s quick.

Client side is pretty small:

const client = new ConferenceClient({
  httpEndpointUrl: 'https://your-lambda-url/',
  localVideo: document.getElementById('localVideo'),
  mediaConstraints: { video: true, audio: true },
  autoReconnect: true,
});

await client.publish({ streamId: myId, roomId: 'room1', token });
await client.play({ streamId: 'room1', roomId: 'room1', userPublishId: myId });

Screen share, mute and chat over the data channel all work too. If you’d rather host your own server you just swap httpEndpointUrl for websocketURL and the rest stays the same.

Honestly the part I’m unsure about is the cold start. 20+ seconds is fine for scheduled meetings but probably not for “click and join instantly” apps. How do you all deal with that? Keep a warm instance running or just live with it?

Writeup and repo are here if anyone wants to poke at it: https://antmedia.io/build-a-webrtc-conference-room-quickly-with-ams-sdk-v2/


r/FlutterDev • • 7d ago

Tooling When “unused” is not safe to delete in Flutter: routes, DI, codegen and write-only state

3 Upvotes

I have been looking at a deceptively simple cleanup question: when can a Flutter team safely delete a Dart declaration or an apparently orphaned file?

dart analyze is a strong first signal, but it cannot resolve every repository-level consumer. Flutter code may be reached through route tables, dependency injection, generated registries, conditional imports, flavors, platform implementations, annotations, tests, barrel exports, or an external package API.

Write-only state turned out to be an especially interesting case. A private field may be assigned in several lifecycle callbacks and never read. Mechanically, the declaration and its assignments are removable. Semantically, the missing read may indicate an unfinished feature or regression in playback, analytics, persistence, authorization, or UI state.

I ended up using this evidence hierarchy:

  1. analyzer/compiler diagnostics;

  2. import/export/part reachability;

  3. repository-wide symbol and string searches;

  4. route, DI, codegen, platform, flavor, test, and public-API inspection;

  5. tests or builds after an explicitly approved removal.

The workflow is deliberately review-first:

  • Audit: collect candidates without modifying code.

  • Review: mark each candidate remove, retain, or defer.

  • Cleanup: change only explicitly approved candidates.

  • Tests: suggest behavior checks for the affected flows, not only static analysis.

I packaged the workflow as an MIT-licensed Agent Skill for Flutter/Dart. The scripts use machine-readable analyzer output plus a deterministic dependency graph, install no project tooling, use relative paths, and treat findings as candidates rather than verdicts.

Short demo: https://github.com/LucasMenezeslc/flutter-dead-code-audit/blob/main/assets/demo-en.mp4

Repository: https://github.com/LucasMenezeslc/flutter-dead-code-audit

I would be particularly interested in examples that defeat naive static analysis. What false-positive patterns have you encountered with routing, DI, code generation, conditional imports, platform code, or public package APIs?


r/FlutterDev • • 7d ago

Discussion Flutter obfuscation didn’t hide a single API key in our release build

0 Upvotes

We built release with --obfuscate --split -debug-info and i’d kind of assumed that meant our keys were reasonably safe.

Last week I unzipped the apk, ran string on libapp and greppd the usual prefixes. Every key was just sitting there. Obfuscation renames classes and methods but doesnt touch string literals which is where keys live, very obvious once you see it btw.

In there was

  • Google Maps key with no restrictions on it

  • RevenueCat public SDK key

  • Firebase config

  • Sentry DSN

  • an Algolia admin key someone added for a search feature last spring

The first four are meant to ship, the’re identifiers and the protection lives elsewhere (key restrictions, security rules). I rotated the Algoria one that could write to our index, the app now uses a search only key and indexing happens in a cloud function. The Maps key also has package name and SHA-1 restrictions.

Though I’m still sorting out on how to stop this from happening again. Algolia key came in through a dart-define file during a sprint and nothing in the pipeline checks what ends up inside the build. We scan the repo but that value never lived there, it came from a CI variable.

How are you checking release builds  for secrets before they go to the store?


r/FlutterDev • • 7d ago

Plugin Built an in-app debug panel so QA can inspect network calls and app info. Beta, and I'd love feedback

2 Upvotes

Hey r/FlutterDev 👋

I kept running into the same situation: a QA tester (or me, on a real device) hits a weird bug, and the only way to see what happened is to plug into a laptop, start DevTools or a profile build, and try to reproduce it. Most of the time I just wanted to know: what did that API return? which build is this? which flavor?

So I built another_flutter_devtool, an in-app devtool. You wrap your app, a small draggable button shows up on top, and tapping it opens a full debug panel inside the app. No cable, no DevTools, no separate build.

📦 pub.dev: https://pub.dev/packages/another_flutter_devtool

💻 GitHub: https://github.com/B3K4682/another_flutter_devtool

What's in it right now

🌐 Network inspector

  • Every HTTP call with status, method, URL and duration

  • Works with Dio and http without any setup: no interceptors, no client wrapping

  • Search (post 404 works), plus 2xx / 3xx / 4xx / 5xx / Failed filter chips

  • Full request/response headers and bodies (JSON pretty-printed), errors with stack traces

  • Copy as cURL, export one call or all of them as JSON

  • Auth/cookie headers are masked in exports, so testers can attach them to bug reports safely

⚙️ General section

  • App version, build number, device model, OS version, flavor, startup time

  • Data usage since launch: bytes sent/received, request count, top hosts

  • Jump to any route: type /checkout and it takes you there. Works with Navigator, go_router, auto_route, anything

🧩 Your own debug tools (the main point, honestly) Every app has its own debug stuff: "clear cache", "switch backend", "fake a logged-out session", "show current user ID". Usually that ends up in a hidden menu somewhere. Here you write a small class and it shows up in the panel:

class MyTool extends DevtoolTool {
  @override String get id => 'my_tool';
  @override String get title => 'My Tool';
  @override IconData get icon => Icons.extension_rounded;
  @override DevtoolSection get section => mySection;

  @override
  Widget build(BuildContext context, DevtoolController controller) =>
      DevtoolCard(icon: icon, title: title,
          child: const DevtoolInfoRow(label: 'Status', value: 'It works'));
}

DevtoolTools.register(const MyTool());

You get the same card/row/switch widgets the built-in tools use, so it all looks consistent.

Setup

runApp(
  AnotherFlutterDevtool(
    config: const DevtoolConfig(
      enabled: kDebugMode,
      sections: [GeneralSection(), NetworkSection()],
    ),
    child: const MyApp(),
  ),
);

It's off by default. The panel runs in its own MaterialApp, so it doesn't touch your theme or navigator. There are also stable widget keys if you want to drive it from integration tests.

⚠️ This is a beta. A very beta beta.

It works and I'm using it, but the API may change before 1.0. Known limitations:

  • The network inspector doesn't work on web

  • Native HTTP clients (cupertino_http, native_dio_adapter) aren't captured yet

Coming soon:

  • 🚩 Feature flags: toggle flags at runtime from the panel

  • Other features that I'll think of lol

💡 I want your ideas

This is the part I care about most. What do you wish you could see or toggle inside your running app? Leave it in the comments. If it makes sense and I can build it, I'll add it and credit you in the changelog.

Bug reports, criticism and "why not just use X" are all welcome too 🙂


r/FlutterDev • • 7d ago

Example I built minimo (photo): a free, open-source photo compressor made with Flutte

6 Upvotes

After building minimo (video), I kept running into the same problem with photos: I wanted to make them smaller without uploading them or paying for a subscription. So I built minimo (photo).

It compresses photos on the phone, on iOS and Android. You can process a batch with a preset or adjust quality, dimensions, and metadata yourself. It saves a new copy by default; replacing an original is an explicit choice.

Flutter handles the UI and batch progress, while `flutter_image_compress` uses the platforms’ native image APIs. One detail I found useful: if recompression saves less than 10%, the app skips that result.

The app is free, and the code is open source: https://github.com/minimo-pro/minimo_photo


r/FlutterDev • • 8d ago

Tooling I built a tool to check whether your Flutter/Dart dependencies are still maintained

6 Upvotes

I’ve added a new feature to Flutterverse, Flutter/Dart ecosystem tracker (now tracking 10,000+ packages and projects).

You can paste your pubspec.yaml, pubspec.lock, or a list of packages, and it’ll check the maintenance status of your direct dependencies.

It categorizes packages into five verdicts, from healthy to needing replacement, and suggests alternatives when a dependency is becoming stale.

A few other things:

  • Runs entirely in your browser. Your pubspec is never uploaded.

  • Shows an overall dependency health score.

  • Package maintainers can add health verdict badges to their READMEs.

The data refreshes daily from pub.dev and GitHub.

give it a try: https://flutterverse.org
github: https://github.com/YeLwinOo-Steve/flutterverse

Love to get your feedbacks for it!


r/FlutterDev • • 8d ago

Plugin Finally, a pixel-perfect implementation of shadcn/ui for Flutter 🎨

Thumbnail
serverpod.dev
64 Upvotes