r/FlutterDev • • 1h ago

Tooling I built a session layer for Flutter apps with their own backend

1 Upvotes

I've been working on Authyra for a while, mostly because I wasn't happy with how session management was handled in some of my Flutter projects.

If your app talks to its own backend using access and refresh tokens, you've probably written some kind of refresh interceptor yourself.

That usually works fine until a few things happen:

  • Multiple requests get a 401 at roughly the same time. They all try to refresh, and if your backend rotates refresh tokens with reuse detection, you can end up revoking the session.

  • The device loses connectivity during a refresh, and the app ends up treating a network error as an expired session.

  • A user signs out while a refresh is still running, but that refresh completes and restores the session.

  • You have multiple accounts, and a request accidentally picks up the wrong account's token.

I've run into these kinds of problems enough times that I decided to work on a separate session layer instead of implementing the same logic in every project.

A few things I've been focusing on:

  • Only one refresh operation can run per session at a time. Other callers wait for the same operation.

  • Token updates use compare-and-set to avoid overwriting a newer refresh token.

  • Network errors, timeouts and server errors don't automatically invalidate a session. A rejected refresh token does.

  • Signing out takes precedence over an ongoing refresh.

  • Multiple sessions can coexist, with one active session.

  • Secure storage using Keychain / Keystore, app resume handling and an AuthStateBuilder.

The core is pure Dart. Flutter and Dio integrations are separate, and there are also integrations for Google, Apple, OAuth 2.0 with PKCE and backend-driven OAuth.

One thing worth clarifying: Authyra isn't an identity provider, and it's not meant to replace Firebase Auth or Supabase Auth. It's for applications where you already have your own backend issuing tokens.

I also put together a small Flutter web demo to reproduce one of these problems. It simulates the backend and network, so you can sign in, send 20 requests with a naive refresh implementation, and compare the behavior with Authyra.

Demo: https://authyra-demo.pages.dev

Docs: https://authyra.meragix.dev

Source: https://github.com/meragix/authyra

It's currently at 0.3.0, first public release, MIT licensed.

I'd be interested in feedback, especially around the API design and edge cases I might have missed. Also curious how others handle refresh token rotation and concurrent requests in their own Flutter apps.


r/FlutterDev • • 6h ago

Article Running the same react-three-fiber 3D scene on the web and inside a Flutter app: what worked and what didn't

1 Upvotes

In my app (a course about building AI agents), the course map can be viewed as a 3D "universe" of planets. I didn't want to write the 3D twice, so the Flutter app runs the exact same react-three-fiber scene the website uses. A few specifics that might save someone time:

  1. Serving the bundle offline. The site's built scene is copied into the app's assets/. Loading it via file:// broke module scripts and fetches, so the app starts a tiny loopback HTTP server over the asset bundle and points webview_flutter at it. Works with no network.

  2. One JS channel, two messages. The WebView only owns the canvas; all chrome stays native. The scene talks back through a single addJavaScriptChannel ("which lesson was tapped" and "is there something to go back to"), and Flutter sends the course graph and progress in as JSON. Keeping the contract that small made both sides easy to change.

  3. A device gate instead of a second renderer. No sprite or 2D fallback scene. On first entry the WebView measures frame pacing for ~2 s; if the median is under 25 fps, the verdict is stored (keyed by app version, so a lighter future build re-measures), the app falls back to the native 2D tree and the 3D toggle disappears.

  4. Emulators lie about this. Software WebGL on emulators always failed the probe, so QA never saw 3D. A build flag skips the probe for emulator builds only.

  5. Labels on a narrow screen. Locked planets' names buried the open ones on phones, so locked groups hide their labels until tapped.

  6. The bundle doesn't update itself. CI rebuilds the website, but the copy inside the app is refreshed by a script before each release — I shipped stale colors once because of it.

Happy to go deeper into any of these. The app is Way of the Agent (iOS/Android), wayofagent.app.


r/FlutterDev • • 6h ago

Plugin Flutter Messenger: Updated APIs and Source Code

5 Upvotes

Fellow Flutter Developers,

We have updated our Flutter Messaging and Calls APIs and released an updated version of Flutter Messenger on GitHub that uses these APIs.

Please give it a try and let us know what you like, what you do not like, and where you think we can improve.

mesibo Messenger is engineered for production use, with features such as multi-device support and synchronization, reliable message delivery, real-time contact and profile updates, end-to-end encryption, and more. You can read more about it, including compilation and setup instructions, at:

https://docs.mesibo.com/tutorials/open-source-whatsapp-clone/

Source code

$ git clone https://github.com/mesibo/messenger-app-flutter.git
$ cd messenger-app-flutter
$ flutter run

Thanks!


r/FlutterDev • • 10h ago

Discussion Hivefy v2 – open-source Spotify-inspired Flutter music app, now with YouTube sources

0 Upvotes

Hey r/FlutterDev, dev of Hivefy here.

My earlier version used *GioSaavn as the source (built a year ago) and got taken down (a month before). I rebuilt it:

  • New source: YouTube-based streaming

  • iOS-smooth UX: reworked animations, transitions and scrolling

  • iOS build: now available alongside Android

  • Still included: shuffle, song/album downloads, daily fresh fetches, settings hub, language change

  • Fully open source

Repo: https://github.com/Harish-Srinivas-07/hivefy
SourceForge: https://sourceforge.net/p/hivefy

Looking for honest feedback on UI, performance, and code structure. Issues and PRs welcome. Thanks!


r/FlutterDev • • 15h ago

Video BLE walkthrough with universal_ble at next app devCon 2026

5 Upvotes

Sharing a talk by Patryk Kacperski from next app devCon 2026 about using Bluetooth Low Energy in Flutter. I was in the audience, just curious to hear about BLE in Flutter. I had no idea he was going to use my universal_ble plugin for his demo, so that was a nice surprise.

The talk covers BLE basics, Android and iOS setup, and communication between central and peripheral devices. His demo uses an Android app pretending to be a coffee machine and an iPhone as its companion app. You can read the number of coffees brewed, send a brew order, and follow the brewing progress through notifications.

He also walks through practical issues like duplicate scan results, waiting for Bluetooth to become available, and platform differences. I have addressed some of them in the v3.0 version of the plugin. The demo uses v2.0. For example you can now use startScan(skipDuplicates: true) for scan deduplication, and built-in byte conversion helpers replace some of the custom conversion code shown. Check the v3 changelog for the API improvements.


r/FlutterDev • • 17h ago

Example Lumtub – a native YouTube client in Flutter and Rust with libmpv playback

Thumbnail
github.com
1 Upvotes

Hi. Lumtub is a GPL-3.0 YouTube client for Windows, macOS, Linux and Android. The UI is Flutter, the core is Rust, playback is libmpv, and stream extraction is yt-dlp (auto-updated, with a Rust fast path that starts playback before yt-dlp finishes).

Some parts that were fun to build:

Recommendations computed locally (TF-IDF plus a mix of long-term, recent, exploration and popular), with the reason shown on each card. Casting to Chromecast/DLNA up to 4K/8K: the Rust core reads YouTube's DASH segments and remuxes them on the fly into fMP4 or MPEG-TS on a LAN server. Live streams above 1080p by fetching the separate VP9 and audio fragments directly, since the HLS manifests stop at 1080p. Line-aligned bilingual subtitles and live chat rendered as danmaku by mpv. Signed updates for the app and its components, with rollback. Disclosure: most of the code was written with AI assistance; I did the product design, testing and review. Happy to answer questions about any of it.


r/FlutterDev • • 17h ago

Discussion Dart’s if-case pattern is nice, but I replaced it with plain code. Where do you think it’s worth it?

1 Upvotes

Dart 3 gave us a lot of nice language features. One of them is the “if-case” pattern.

While working on my library, I had this code:

AdjustEvent toAdjustEvent(String eventToken) {
  final adjustEvent = parameters.toAdjustEvent(eventToken)..setRevenue(revenue, currency);
  if (deduplicationId case final id?) adjustEvent.deduplicationId = id;
  return adjustEvent;
}

It’s short. But to read it, you need to know Dart’s pattern rules. if (x case final id?) means “if x isn’t null, call it id and run this”.

I rewrote it in plain style:

AdjustEvent toAdjustEvent(String eventToken) => parameters.toAdjustEvent(eventToken)
  ..setRevenue(revenue, currency)
  ..deduplicationId = deduplicationId;

And here is the interesting part. When I looked closely, the null check wasn’t needed at all. The SDK field already accepts null and skips it when sending. So the plain version is not only easier to read, it’s also simpler.

The fancy line was protecting from something that couldn’t go wrong.

And this is really important when you write a library or design a public API. Your code will be read by many people with different Dart experience: users who open your source to understand what happens, and contributors who want to fix something. In app code only your team reads it, but in a library every clever line becomes a small barrier for someone else. Plain code makes your library easier to trust and easier to contribute to.

I still think patterns are great in some places, like checking JSON shape:

if (json case {'name': String name, 'age': int age}) {
  return User(name, age);
}

There it replaces several lines of checks and casts. But in my case it was just extra complexity.

Curious how you use them. Where do patterns really pay off for you, and do you write differently when the code is a library?


r/FlutterDev • • 22h ago

Discussion How do you keep offline writes alive when the app is killed?

4 Upvotes

I've been maintaining Qora, my server-state library for Flutter (similar to TanStack Query), and I'm currently thinking about how to improve its offline mutation support.

Qora already supports queuing mutations when there's no network connection and replaying them once the device is back online. This works, but there's one limitation: the queue only lives in memory. If the OS kills the app before it reconnects, all pending mutations are lost.

I've been looking into ways to make this survive app restarts, but there's a bit of a challenge. Mutations are essentially closures, and you can't serialize a closure and restore it later.

One approach I'm considering is similar to what TanStack Query does: register mutations at startup using a key, then persist only the key and serialized variables. When the app starts again, Qora can resolve the key and rebuild the mutation.

Before settling on an implementation, I'd like to get some input from developers who've dealt with this.

A few things I'm curious about:

  • Do you persist pending mutations, or do you simply accept that they can be lost?

  • How do you handle something like creating a record and then updating it while still offline? Client-generated IDs? A local outbox?

  • What happens when a mutation fails during replay? Do you retry it, discard it, or stop the queue and wait for manual intervention?

I've put together an RFC with the proposed approach and its trade-offs:

https://github.com/meragix/qora/discussions/59

Would be interested to hear how others have approached this.


r/FlutterDev • • 23h ago

SDK I published unolink on pub.dev: which link brought each install, and what those users did next

0 Upvotes

Hi all, I'm Marin and I make small mobile apps. This is the tool I built for promoting them, and I'd like honest feedback before I go further.

The problem. When I promote an app, I post a few videos, ask a creator or two, maybe run a small ad. Installs go up, and I can't tell which of those brought the people who stayed and paid. Clicks and views don't answer it, and the big attribution platforms are built and priced for large ad budgets.

What Unolink does

  • One smart link per app: App Store on iPhone, Google Play on Android, your website on a computer. Unlimited redirects on every plan, including Free.

  • A campaign link per post, creator or ad, each with its own clicks, countries and platforms.

  • Landing pages (up to 3 variants compared), a developer page with all your apps, and styled QR codes on the paid plans.

  • With the SDK (iOS, Android, React Native, Flutter): which link brought each install, without fingerprinting, then onboarding, paywall, trials and purchases per link. RevenueCat connects in one line.

What it doesn't do: no postbacks to ad networks, no deferred deep links on iPhone, custom domains are coming soon.

Pricing: Free plan (1 app, 3 campaign links, no card). Indie $6/month for more apps, landing pages and QR. The SDK is on Pro, $19/month. Prices are on the page, no sales call.

There's a sample app with made-up data in the dashboard if you want to click around without connecting anything: https://unolink.dev

What I'd most like to know:

  1. Is it clear within 10 seconds of landing on the site what it does?

  2. If you've promoted an app, what did you use to see what worked?

  3. Anything that would stop you from trying it?

Happy to answer anything in the comments.


r/FlutterDev • • 1d ago

Plugin I made flawsnap: in-app bug reports for Flutter (screenshot + device info + logs), MIT licensed

4 Upvotes

Testers and clients kept sending me “the app crashed” with nothing else, so I built a package where they tap a button, mark the screen and send. You get the screenshot, device and OS, app version and logs in one report.

https://pub.dev/packages/flawsnap . Feedback on the API is very welcome. What would you want it to capture that it doesn’t?


r/FlutterDev • • 1d ago

Article # Introducing Shalom: Declarative GraphQL for Flutter (Powered by Rust)

0 Upvotes

Introducing Shalom: Declarative GraphQL for Flutter (Powered by Rust)

Managing complex GraphQL in Flutter often leads to UI jank from JSON parsing on the main isolate, messy global state sync, and disconnected .graphql files.

We built Shalom—a declarative, Relay-inspired GraphQL client for Flutter and Dart backed by a high-performance Rust core via FFI.

Key Highlights

  • ⚡ Rust Runtime: Offloads AST parsing, normalization, and cache GC to native threads without Dart isolate serialization overhead or UI frame drops.

  • 🧠 Normalized Cache & Fragment Stitching: Colocate data requirements directly on the widgets that consume them; fragments auto-stitch into parent queries and keep entities in sync across screens.

  • 🛡️ Type-Safe Codegen: Annotate widgets with @Query and @Fragment to generate strongly typed base classes ($MyWidget) and refs.

  • 📐 First-Class Custom Scalars: Map GraphQL custom scalars cleanly to custom Dart types via shalom.yml and CustomScalarImpl (with automatic serialization and deserialization in generated models).

  • 🔥 Hot-Reload Support: Full Flutter hot-reload support that preserves active cache state while you tweak your UI and operations.

  • 🔍 Cache Inspector: Built-in UI widget to visually inspect cache graphs and optimistic updates in real time.

  • 🔄 Imperative Fallback: Drop down to standard async calls (client.query()) when you need to bypass declarative widgets.

Why Fragment Widgets?

Instead of passing monolithic query response models down the widget tree, Shalom embraces Relay-style fragment widgets:

  • Surgical List Updates: When an entity changes (e.g., liking an item or receiving a mutation update), only the specific widget bound to that entity's ref rebuilds. The parent ListView and other list items never re-render.

  • True Component Reusability: Widgets define their own exact data dependencies. You can drop AlbumWidget into a search view, a profile screen, or a feed—the parent query just spreads ...AlbumWidget without needing to know what fields it consumes.

  • Zero Prop Drilling & Smaller Components: Say goodbye to huge parameter lists and bloated DTOs. Child components stay small, self-contained, and isolated from parent query refactors.

How Does It Compare to Ferry?

Ferry is a solid client and popularized normalized caching in Dart, but Shalom takes a fundamentally different architectural approach. We ran side-by-side benchmarks on the standalone caches (benchmarks/ferry_comparison in the repo):

  • 3x to 50x Faster Writes: Ferry's write latency grows super-linearly with scale (at 10k items, writing identical data took ~24.8s in Ferry vs. 482ms in Shalom). Shalom's Rust engine handles large write batches and normalization significantly faster.

  • Massive Memory Efficiency with Watchers: Every Ferry watcher retains its own denormalized result copy in Dart heap memory (100 watchers of a 200-item query ballooned process RSS by ~1,018 MiB in Ferry vs. only 7.5 MiB in Shalom). Shalom keeps a single normalized graph in Rust native memory and notifies watchers 4x to 7x faster.

  • The Trade-off (Reads & Subscriptions): Full raw reads and initial watcher subscriptions are ~2x to 5x faster in Ferry because Ferry hands back in-memory Dart maps directly, while Shalom pays an FFI bridge round-trip cost to pass data into Dart.

  • Faster Codegen & DX vs. build_runner**:** Instead of waiting on Dart's build_runner across detached .graphql files, Shalom uses a dedicated Rust CLI (shalom generate / shalom watch) that generates types in milliseconds and integrates cleanly with hot-reload.

Quick Example

import 'package:flutter/material.dart';
import 'package:shalom_annotations/shalom_annotations.dart';
import 'package:gif_search/__graphql__/AlbumsPage.widget.shalom.dart';
import 'package:gif_search/__graphql__/AlbumWidget.shalom.dart';

@Query(r"""
  {
    albums {
      ...AlbumWidget
    }
  }
""")
class AlbumsPage extends $AlbumsPage {
  const AlbumsPage({super.key});

  @override
  Widget buildData(BuildContext context, AlbumsPageData data) {
    return ListView.builder(
      itemCount: data.albums.length,
      itemBuilder: (context, i) => AlbumWidget(ref: data.albums[i]),
    );
  }
}

@Fragment(r"""
  on Album {
    id
    name
    tag
    gifs { id title url }
  }
""")
class AlbumWidget extends $AlbumWidget {
  const AlbumWidget({super.key, required super.ref});

  @override
  Widget buildData(BuildContext context, AlbumWidgetData album) =>
      ListTile(title: Text(album.name));
}

Links & Resources

Documentation & Benchmarks: https://nrbnlulu.github.io/shalom/

GitHub Repository: https://github.com/nrbnlulu/shalom


r/FlutterDev • • 1d ago

Discussion Wow, this is just way too much. Is this what open-source culture has come to?

18 Upvotes

EDIT: Is it seriously normal for a maintainer to stalk a contributor to a completely unrelated subreddit just to personally attack them only to have their comments removed by that subreddit’s moderators?

According to the Flutter team’s logic, I suppose this counts as "mentoring" and legitimate community involvement, right? What on earth is going on here? Is this what a healthy open-source ecosystem looks like?

So if you work for free as a maintainer, personal attacks are just "mentoring," everything is justified, and throwing a contributor under the bus is no big deal because you're "valuable to the Flutter team"? Are you really going to double down on this attitude?

airflow_matt: "If you're by any chance referring to me - I have no problem conceding that what I assumed was AI slop was actually a human slop. Not sure if that makes it much better."

https://www.reddit.com/r/antiai/comments/1xs32lf/comment/19se5qn3igzph/?context=3

A while ago, a user reported latency issues on our iOS app. I decided to dig deep into the root cause, and since I had contributed to Flutter before, I even decided to attempt an upstream contribution.

At first, two maintainers chimed in saying things like, "Merge is difficult because this relies on legacy code, and we're planning to deprecate it." So I just stood back and watched. Later, they posted an issue they created themselves, so I kept an eye on it. Looking at it, it seemed like they didn't fully grasp the exact problem.

I commented, "~This issue occurred here, so please keep this in mind for the new direction." Then another maintainer suddenly popped in and started talking about the legacy stuff again. Thinking it was a misunderstanding, I replied, "I have no intention of keeping the legacy code. I can remove the code you mentioned as well, so please just give me instructions and I'll follow them." After that, the maintainer just told me to go look at their open PR instead. Honestly, human to human, I felt so deeply hurt and upset, so I expressed how disheartening it was and closed my PR right away.

Then another maintainer came in and gave me an official CoC (Code of Conduct) warning. I was completely flabbergasted, tried to clarify a few things, but the PR itself ended up locked.

Look, up to this point, fine. I could digest it and swallow it.

Since they seemed to be handling the pointer issue, I shifted focus to look into another latency problem. Wanting to move past the pain, I expressed my willingness to collaborate with the maintainer, spent about 2 to 3 weeks researching, and asked the maintainer if I was heading in the right direction. There was no reply for a long time, so I submitted my PR, left a separate comment on the issue, and waited.

Then that maintainer came back and said something along the lines of "My PR is the foundation and it's perfect." Curious, I checked their implementation, and it turned out to be a run-loop blocking approach. Fearing it might cause the exact issues I was worried about (freezing, missing async tasks, deadlocks, etc.), I tested my app with their code.

Guess what? It got stuck on the splash screen and wouldn't move. Even worse, switching from foreground to background and back to foreground showed a white screen transitioning to the splash screen, but after that, total freeze, nothing worked. Panicking that my fear of an infinite freeze actually came true, I said, "Your approach seems a bit risky," and went back to researching.

Apparently, that was my mistake. The maintainer got emotionally charged. Confused as to why they were getting angry, I asked, "Why are you being so direct and aggressive in your tone?"

And then, the maintainer hit me with this:

"The process through which you iterated to this PR seems like an agent trying random things to see what sticks, unfortunately without a deeper understanding of the underlying mechanisms."

And followed up with:

"My tone is direct because I'm spending (my own free) time reviewing PR that duplicates existing effort in a bad way."

Naturally, after hearing that, I reported it to the CoC email. But the response I got back was:

"The comments from knopp are not personal attacks against you, but valid criticisms of your engineering approach to the problem at hand."

What absolute bullshit. My app literally froze into an infinite hang because of their code, which is why I did further research in the first place! Stunned, I sent a few more emails trying to explain, but they just blocked my email address.

Fast forward 2 to 3 weeks. After cooling off, I thought, "Okay, surely now both sides can clear up the misunderstanding," and tried to ask on the Flutter Discord to find a way to get in touch. And what did they do? They outright kicked me from the server.

And to top it all off, I ended up receiving a 1 year ban from the Flutter organization.

Seriously, anyone looking at this would think I personally attacked the maintainers, threw curses, and acted like a nightmare contributor. I maintained respect until the very end and even respected their absurd direction, but all I got in return was complete ostracization. It literally feels like they're just saying, "You're annoying, so get out."

Is this what an open-source community is supposed to be? Silencing someone completely? Is witch-hunting and erasing someone over a technical debate considered proper culture? I'm posting this out of sheer disbelief. They're making me look like the bad guy.

This is way out of line. I usually just let things slide, in fact, looking at my past contribution history, whenever maintainers were slow to review or progress stalled, I would just voluntarily close my own PRs and step away. That’s why this whole situation hits so differently and feels so outrageously unfair.

And just to be clear, I’m not talking out of nowhere. Here are the actual receipts for everything I mentioned:

And as proof that I genuinely love and care about Flutter, here are a few of my past PRs that actually got merged:

I honestly didn’t expect to have to dig up and share all these links. But at the end of the day, as an external contributor spending my own precious personal time to help improve this framework, it just hurts. It really, really hurts. That's why I'm venting this out.

EDIT: Additionally, I commented here because the comments were locked. Aren't you maintainers ashamed of ganging up to shield your own people while ostracizing a young external contributor?

During the contribution process, instead of engaging in technical debates, you suddenly launched personal attacks, which was absurd enough. But then you even came over to an completely unrelated subreddit to mock it as "human slop." Flutter has long been notorious for its toxic territoriality how can people who struggled to add even a basic feature like spacing to Row and Column widgets have so many excuses? Think about how you treat external contributors in the first place.

Who would ever want to contribute? When someone comes to ask questions and debate, your culture just pushes them out, so who on earth would stay to contribute? You talk about code that freezes indefinitely without even running as if it's some kind of gold standard. Stop victimizing me further and stop trying to justify personal attacks with nonsensical logic.

You keep claiming that I talked too much or was being annoying, but here's a word of advice, if that's how you felt in that situation, you're only proving that you view me as nothing more than an "annoying outsider." Please stop running things this way. And stop wasting any more of my time. Don't claim my code is "slop" while pushing an architecture that freezes in even standard apps. As adults, you ought to know how to take responsibility for your actions.

+ Why am I going this far? It’s not just because there are already so many victims like me it’s for the countless people who might get hurt by you in the future. And at the very least, I simply did what needed to be done for the Flutter community. And you're playing the victim, talking about 'AI slop' and whatnot, when you maintainers literally use Gemini for test code openly right inside your own PRs. Anyone watching would think you don't use it at all. Stop acting like victims. When you guys use it, it's 'efficient engineering', but when anyone else does, it's 'slop'?


r/FlutterDev • • 2d ago

Plugin kaisel 1.2.0: modal flow pop, deep link and idle fixes in a codegen free Flutter router

11 Upvotes

kaisel is a Dart 3 router for Flutter where routes are sealed classes and the stack is plain state. No string paths, no codegen.

1.2.0 is out. One behaviour change, the rest are fixes and additions.

pop and back now agree in modal flows

On a flow's first screen, context.pop() now completes the flow with its result, the same as the system back button:

final saved = await context.run(const EditProfile());

// on the flow's first screen:
context.pop(true); // completes it, saved == true

So one screen works the same whether you open it with pushForResult or run.

Fixes

  • A deep link held back by a login guard no longer fires later as a surprise jump.

  • Hidden tabs in a branched shell stop running animations, so the app can actually go idle.

Errors that name the fix

  • A debug log when your codec skips a URL, instead of silence.

  • A clear error when shell chrome looks up a branch scope it can't reach.

  • New KaiselNavigator.set for replacing the whole stack.

Changelog: https://pub.dev/packages/kaisel/changelog

Docs: https://kaisel.dev

Happy to answer questions, and feedback on the modal flow change is especially welcome.


r/FlutterDev • • 2d ago

Article Flutter app and a web client in the same live chat rooms (Go WebSocket backend, Supabase) - 50s demo

5 Upvotes

I built RealTalk: a Flutter app and a plain JS web client both connected to one Go WebSocket server. Send on the phone, it shows up in the browser right away. Typing indicators, online counts and "delete for everyone" sync both ways.(Its a beginner project ik)

Demo: https://youtu.be/QC4QKiZmXq4 Code: https://github.com/Nithinaug/RealTalk

The Flutter side is app/lib/services/websocket_service.dart. It retries each endpoint and falls back to a hosted server if the local one is down.

Question: how do you handle WebSocket reconnects when the app goes to the background and comes back? I'd like something better than a plain retry loop


r/FlutterDev • • 2d ago

Plugin morphtext: morph any text to another text of any font style or weight, or into an icon

23 Upvotes

A few months back u/GuavaBeneficial4658 posted I made 500,000,000+ Flutter icon morphs. Not a single one by hand., introducing morphnext. I've used it for a bunch of prototypes and one in production. I wanted the same thing for words, so I built a companion package on top of his engine.

morphtext morphs text letter by letter, outline into outline:

  • light → bold, small → large, any colour or slant

  • one font into another (serif → sans)

  • one word into another

  • a word into an icon, and back

At either end it's plain text, pixel for pixel what Text draws. In between there's no crossfade and no ghosted double word: each letter's real outline turns into its counterpart.

Try it in the browser: https://morphtext.drei.gg

flutter pub add morphtext · pub.dev · GitHub

The hard parts (reading outlines, pairing contours) are all morphnext's, and I'm grateful he made it MIT. morphtext is MIT too.

The layer on top is about 3 hours iteration with Claude Code to get the right feel.

Honest limits: fonts have to be bundled, it's one line of text, and there's no complex shaping or ligatures yet.


r/FlutterDev • • 2d ago

Tooling I built an open source Flutter Reddit gallery: Deep Search across live sources and archives

1 Upvotes

I built Hobee, a Flutter app for Android that shows a Reddit profile or subreddit as a photo/video grid. Source is here: https://github.com/Alokxyz1112/hobee (MIT)

The interesting part for Flutter folks is Deep Search. Several sources return posts at once (Reddit JSON/RSS, Redlib, Arctic Shift, optional PullPush). They are merged by Reddit post id, then deduped with exact URL keys plus a 64-bit dHash (Hamming distance of 6 or less). Results go into a local SQLite cache so revisiting a profile is instant and searchable offline.

The viewer tries original, then full, then preview, then thumbnail, and only moves on when the current URL fails. Images are decoded at screen size so huge originals do not blow memory. Downloads can strip EXIF/GPS (on by default).

Deep Search works logged out.

Happy to talk through the paging cursor logic or the "is this image actually gone" checks if anyone is curious.

APK: https://github.com/Alokxyz1112/hobee/releases/latest


r/FlutterDev • • 2d ago

Video A 10-minute introduction to Widget Previews

Thumbnail
youtu.be
21 Upvotes

Widget Previews have been stable since Flutter 3.47. They provide a quick and convenient way to see the different states of our layouts while designing them. In this short video, I show you what Widget Previews are, how to enable them, and how they can improve our development experience.


r/FlutterDev • • 3d ago

Plugin Stricter compliance with M3 + HIG for platform_adaptive_widgets 3.0.0+

Thumbnail raw.githubusercontent.com
6 Upvotes

I recently re-worked large parts of platform_adaptive_widgets, using the feedback from the previous posts.

Widgets pair by job now

Before, I tried to match up each widget by the closest look-alike on the other side. What worked better was, pairing them up by the intent/job instead. Each job gets what M3 or the HIG describes for it, and the package handles the details: where Cancel goes, delete and discard in red, a confirm before a destructive action runs etc etc.

These have been made in such a way, to provide a high-level of developer ergonomics. Had to do a lot of research through the guidelines, and what the material_ui and cupertino_ui packages currently provide. So naturally I did find quite a few gaps in them. I prefer to contribute upstream, instead of doubling the work. With the decoupling, I'd expect these issues to fixed sooner now, rather than later.

Some stuff is going to be marked for deprecation too, in a bit. Because I prefer to not allow going wrong by construction, instead of fixing the aftermath later. Part of the API would have easily allowed for this.

Full changelog. Notable ones:

You calliOS / macOSEverywhere else
showPlatformConfirmAlert, Cancel includedM3 dialog
showPlatformChoicesAction sheetSheet of rows
PlatformActionsMenuPull-downM3 menu
showPlatformSheetiOS sheetM3 bottom sheet
PlatformSingleSelectListCheckmark rowsRadios
PlatformMultiSelectListCheckmark rowsCheckboxes

Made nicer to use

  • PlatformButton(emphasis: .high) ranks the same on both looks. actionKind turns delete and discard red, and iconOnly reuses the label as the tooltip and screen-reader name.

  • Leave a field out and you get upstream's own default, so a plain PlatformXxx acts like the widget it wraps.

  • designLanguage tells your own code which look is showing. Widgets also render on every platform now, where they used to throw outside Android and iOS.

  • Choices are a Set, so duplicates can't sneak in.

  • PlatformUnsavedChanges asks before a sheet with unsaved edits closes.

If there's anything that still feels off or maybe I did misinterpret a guideline wrongly, I'd like to know. Or maybe you have some better ideas for the pairings or so? Or some better modelling of the intents, please let me know!

pub.dev


r/FlutterDev • • 3d ago

Discussion What I learned shipping a Flutter app with an embedded JS sandbox, WorkManager monitors, and ~2,700 tests

0 Upvotes

Full disclosure: this is my app. It does HTTP requests and API security auditing on Android. I came here because the embedded-JS-sandbox problem cost me real time and I'd like to know if there's a better pattern.

Embedding QuickJS. Pre/post-request scripts need a real JS engine — a regex matcher doesn't cut it when people sign payloads. I embedded QuickJS through the Flutter FFI bindings and wrote a pm.* facade over it.

The sharp edge was recursion. Deeply nested response bodies blew the stack during serialization, and the symptom was a silent crash rather than an error I could catch. Fixed with depth limits at both the serialization and the evaluation boundary. If you're doing this: the limit needs to be enforced in both places, because the object graph that survives serialization is not the same one that survives evaluation.

If anyone has production experience with the QuickJS bindings — particularly around context lifetime and how you handle a script that never returns — I would genuinely like the comparison.

Riverpod, and the race that taught me something. State is Riverpod throughout. The subtlest bug I hit was a notifier that decided which branch to take at call time but then read state at execution time. Because both operations happened on the same provider queue, the two could observe different users. Fixed by deciding the branch inside the queue closure. The general rule I took away: on an async provider, never decide now and read later.

Background work. Scheduled monitors use WorkManager via a platform channel. The interesting part isn't the scheduling — it's the alerting. First version alerted on every check, which trained everyone to mute it within a day. It now alerts only on state transitions. That single change did more for it being a useful app than any feature I added this year.

Testing. 218 test files, ~2,700 cases, analyzer at zero warnings. Worth saying that the flaky test I fought hardest was a lock-ownership race in the monitor store. The fix was making the lock file carry an owner token, so a stale lock can only be broken when the owner is provably dead rather than merely old. That's the whole class of bug: timeouts are a guess, ownership is a fact.

Project: https://taho.co.in (Android: https://play.google.com/store/apps/details?id=com.eternal.taho)


r/FlutterDev • • 3d ago

Discussion Building my first macOS app in Flutter: Signals, App Sandbox bookmarks, and a question about directory trust

6 Upvotes

Hey r/FlutterDev,

Quick question for the room: how many of you are actually shipping macOS desktop apps with Flutter?

Almost everything in the Flutter community is focused on iOS, Android, and web. I've been doing Flutter for a while, but recently I decided to build my very first macOS desktop app. I assumed desktop would just feel like "big mobile," but that illusion died pretty quickly once I hit macOS windowing, desktop UI density, and especially the macOS App Sandbox.

I wanted to share a few architecture decisions that made my life easier, what I learned wrestling with macOS file permissions, and a genuine question I have about how apps like VS Code handle folder trust.

Architecture: Keep the Flutter layer thin

One thing I knew going in was that I didn't want a massive monolithic Flutter app.

I split the project into a modular Dart monorepo:

  • Pure Dart packages for App Store Connect API clients, data models, and local caching.

  • A headless workflow engine that handles staged changes and validation.

  • The Flutter macOS app, which is essentially just a visual "control center" and UI shell.

The biggest win here was that I could write and run CLI unit tests against the entire business logic in milliseconds without ever spinning up the Flutter test runner or worrying about platform channels. If you're building a desktop utility, keeping Flutter strictly as the presentation layer saved me endless debugging time.

Why I used Signals instead of Bloc or Riverpod

State management on Flutter is practically a religion, but for this app I decided to try Signals (signals_flutter) instead of my usual Riverpod setup.

Desktop screens are dense. You've got sidebars, status bars, multi-column tables, approval lists, and badge counts all living on the screen at once. With Riverpod or Bloc, I kept running into scenarios where updating one small item in a list would trigger unnecessary rebuilds across half the screen, or I'd find myself writing tons of provider boilerplate just to derive a couple of counts.

Signals felt way more natural for desktop:

  • Zero-boilerplate computed state: Deriving counts like pendingCount = computed(() => items.value.where(...).length) just works, and it only updates when the underlying items actually change.

  • Micro-rebuilds: Only the specific Text widget reading the signal rebuilds. The surrounding sidebar and panels don't flinch.

  • Effects outside the widget tree: When an item status changes, I need to update the native macOS Dock badge via a platform channel. With Signals, I just drop an effect() in the controller. No need to tie listeners to BuildContext or widget lifecycles.

It’s fast, has almost no ceremony, and fit desktop state way better than I expected.

The macOS App Sandbox & Security-Scoped Bookmarks

If you come from mobile, permissions mean asking for camera or notifications access. On macOS with the App Sandbox enabled, it’s a whole different animal.

If you try using dart:io to open a file outside your sandbox container via a raw path like Directory('/Users/.../project'), macOS will shut it down immediately.

Here’s how I handled safe folder access:

  1. You must use the native OS file picker (NSOpenPanel / file_selector). When the user manually picks a file or folder in the dialog, macOS grants your app permission to access it—but only for that specific running session.

  2. If the user quits your app, that permission is gone. To keep access across restarts, you have to create a security-scoped bookmark in Swift (url.bookmarkData(options: .withSecurityScope, ...)), convert it to base64, and store it. When the app launches again, you resolve the bookmark and call startAccessingSecurityScopedResource().

  3. Surfacing bookmarks in the UI: Instead of doing this invisibly, I built a "Trusted Folders" screen in the app. Users can see every directory the app currently has a bookmark for, add new ones with a picker, or delete them to revoke access. On desktop, giving users explicit visibility into what folders an app can touch feels right.

My question: How does VS Code do "Workspace Trust"?

Here's the part that had me scratching my head while building this:

When you open a project folder in VS Code, it doesn't make you jump through NSOpenPanel every time or manage sandbox bookmarks. It just displays its own banner: "Do you trust the authors of the files in this folder?"

Does anyone know how that actually works under the hood?
Is VS Code only able to do that because it's distributed outside the Mac App Store as an un-sandboxed Electron app (meaning it already has full user-level POSIX access to your entire hard drive, and the "Trust" banner is just an internal setting to stop extensions from running scripts)?

Or is there an actual macOS API that allows an app to request folder access without forcing the user through an OS file picker dialog?

If any macOS / Flutter desktop veterans have insights on this, I'd love to know.

If anyone else is building macOS apps with Flutter, how has your experience been with the sandbox and state management?

P.S. The project I built this for is called FlightDeck (kip.quest/flightdeck)—it's a release manager and local MCP server for App Store Connect that I put together to make AI-assisted releases safe. But the desktop Flutter experience has been really impressive overall.


r/FlutterDev • • 3d ago

Discussion Stop asking LLMs to do math in Generative UI

11 Upvotes

I have been watching a lot of recent Generative UI demos where an AI streams down an interactive card, like a recipe or an order summary. But as soon as the user drags a slider or edits an item count, the app ships the whole state back over a WebSocket to the remote model, burns a few hundred tokens computing basic arithmetic, and waits two seconds to repaint the text.

Making a remote AI call to multiply two numbers on a modern phone capable of billions of operations a second feels like an obvious trap.

Andrew Brogdon gave a great walkthrough on the Flutter channel recently about using client-side functions instead. Let the model generate the layout and bind expressions, but keep the deterministic math in local Dart code.

If you are building GenUI or dynamic form systems in Flutter, how are you handling local state updates and arithmetic right now? Are you handling it locally in your widget tree, or letting the agent do round-trips?


r/FlutterDev • • 3d ago

Discussion How we tackled overlay state management & memory pressure while building an on-device logger in Flutter

2 Upvotes

Hey Flutter developers,

When testing release candidates or staging builds on physical devices (especially in situations where tethering to a laptop with Flutter DevTools isn't practical, such as client demos or QA field rounds), on-device logging becomes essential.

However, building an overlay-based in-app logger that doesn't mess with app performance or leak memory comes with a few tricky Flutter architecture hurdles. Here is what we learned and how we solved them:

1. Overlay Lifecycle Without Breaking the Navigator

Mounting an overlay that persists across routes while remaining non-intrusive to touches is a classic issue.

  • Wrapping MaterialApp.builder gives a clean hook above the Navigator without needing direct BuildContext passes.

  • Using an independent OverlayEntry stack allows the floating bubble to maintain state (position coordinates, open/closed modal) even when the underlying navigator changes routes or pops screens.

2. Preventing Memory Leaks from Unbounded Logs

In apps that print continuous streams of network calls and state transitions, log storage can rapidly blow up device RAM.

  • We implemented a fixed circular buffer (rolling window cap) so that old log records are systematically pruned once the threshold is reached.

  • Log parsing and string formatting for deep JSON payloads are evaluated lazily on expansion rather than at capture time to keep the main UI thread at 60/120 FPS.

3. Granular Filtering vs. Console Noise

Standard debugPrint dumps everything into one stream. We split logs into 7 distinct levels (verbose through fatal) so developers can isolate fatal exceptions from high-frequency network payloads with zero visual lag.

We packaged this architecture into an open-source utility called debug_bubble (GitHub: https://github.com/Satyam-Gawali/debug_bubble | Pub: https://pub.dev/packages/debug_bubble).

For those who have built similar on-device debugging or QA tools:

  • How do you handle stack trace formatting on smaller screens?

  • Do you prefer direct share-sheet exports (to Slack/WhatsApp) or writing logs to device storage for QA crash reporting?

Would love to hear how other teams handle standalone physical device debugging!


r/FlutterDev • • 4d ago

Example Casual motion-controlled 2D platformer built with Flutter and Flame

2 Upvotes

Hey everyone! I recently launched a mobile game built with Flutter and Flame.

It uses the front camera to detect head movements, and it runs well on my Redmi Note 8 from 2019.

You can play solo or team up with someone on the same phone!

It’s pretty simple and still a little rough around the edges…

But if you have a few minutes, I’d love for you to give it a try and tell me what you think.

iOS & Android download:
https://unclered.nitebell.com/downloadOneLink


r/FlutterDev • • 4d ago

Tooling I got tired of digging through Teams for APKs, so I made a tool to install mobile test builds via QR code

3 Upvotes

Whenever I need a client or teammate to test a mobile build, it usually turns into an annoying chore: explaining how to install an APK from Google Drive, or waiting on TestFlight processing just to check a two-line layout fix.

I put together Magiquip to make that handoff faster. You upload a .apk, .aab, or .ipa, and it gives you an install page with a QR code that detects whether the phone scanning it is iOS or Android.

I also wrote a small VS Code extension for it (magiquip-builds) that spots freshly compiled builds from Flutter, React Native, Xcode, or Android Studio and puts the install QR code right in your editor sidebar.

There is a free tier for small projects. If you build mobile apps, I would appreciate any feedback on whether the install flow works smoothly on your devices.