- Platform
- iOS & Android
- Duration
- 16 weeks
- Industry
- Legal & Events
- Read time
- 8 min read
In short
RaftLabs built the Concurrences Events app for a Paris-based legal publisher, delivering native iOS and Android conference discovery and registration in Flutter on top of the client's existing PHP backend. The app includes Firebase-backed real-time attendee chat, in-house offline caching, tokenized URL session sharing between native and website-backed sections, and payment flows engineered for iOS and Android webview constraints. Built in 16 weeks with zero app-side stability issues in more than two years of production. When Twitter's API pricing made a native live-tweets integration unsustainable, RaftLabs replaced it with a zero-maintenance in-app webview before it appeared on a client invoice.
Legal professionals attending Concurrences conferences — events on antitrust law and competition economics held across Europe — had no native way to discover and register for them. The website worked. On a phone, it did not.
Concurrences came to RaftLabs with a clear brief: build an iOS and Android app that sits on top of their existing website and PHP backend rather than replacing it. The client's own developer ran the backend and kept running it throughout the engagement. RaftLabs owned the mobile layer only.
We built that app in Flutter in 16 weeks. Firebase handles real-time attendee chat and push notifications, decoupled entirely from the PHP backend. Tokenized URLs carry the user's authenticated session from native screens into website-backed sections. Content the user has already loaded is cached in-house for offline access. The app has been in production for more than two years. Every post-launch issue has traced back to the client's backend — not the app.

before & after
What changed
- The only way to access Concurrences events on a phone was through the mobile website, with no native interface, no push notifications, and no offline access
- Attendees had no in-app channel to connect with other conference delegates before or during an event
- Content loaded fresh on every visit; returning without a connection showed nothing
- Payment flows designed for desktop browsers did not behave correctly on mobile
- Discovering and registering for events required navigating a full website experience on a small screen
- Native iOS and Android apps give lawyers and delegates a purpose-built interface for discovering and registering for legal conferences worldwide
- Firebase-backed push notifications alert users to new events, deadlines, and session updates without requiring the app to be open
- Real-time attendee chat lets delegates connect before and during conferences, running on Firebase rather than the client's PHP backend
- Previously loaded content is cached in-house and accessible without a connection
- Tokenized URLs carry the authenticated session from the native app into website-backed sections — one sign-in works across both
What we had to solve
- 01
Building real-time chat without touching the client's application backend
The app needed in-event attendee chat so delegates could connect once registered. The primary backend was PHP, owned and operated by the client's own developer. Adding endpoints or changing the data model required coordinating with the client's team — a dependency that would have slowed every future chat feature. The solution was to build the chat layer on Firebase, fully decoupled from the PHP backend. Chat worked without touching anything outside RaftLabs' scope, and future changes to the chat model don't require a backend deployment.
- 02
A live-tweets feature that became expensive because of someone else's business decision
Embedding live event tweets via the Twitter API was in the original specification and worked as intended. After a change in Twitter's ownership and API pricing, running a native integration became prohibitively expensive. The team removed the native Twitter API integration and replaced it with an in-app webview linking to the relevant Twitter pages. The feature's intent was preserved. The ongoing cost was eliminated. No lingering dependency remained.
- 03
Offline caching with full control over what gets stored and when it expires
Delegates need to revisit event details, session schedules, and speaker bios without a guaranteed connection — a realistic scenario for professionals traveling between sessions. Rather than use an external caching package, the team built the offline caching layer in-house. That decision cost more time upfront but gave complete control over what was cached, how long it was kept, and how it was invalidated when content changed. For content that updates on a schedule — agendas, speaker profiles — that control matters.
- 04
Session identity that follows a user from native screens into website-backed sections
Some sections of the app — user account management, certain registration flows — open the Concurrences website inside a webview. A user logged in to the native app expects to arrive at those sections already authenticated, without a second login prompt. This was solved with tokenized URLs: the native app generates a token encoding the user's identity and passes it as a parameter when launching any website-backed section. The Concurrences website validates the token and establishes the session. The user sees one continuous experience; the mechanism is explicit and auditable.
- 05
Making desktop payment flows work inside a mobile webview
Concurrences' event registration payments ran through infrastructure designed for a desktop browser — standard HTML forms, browser-level payment APIs, and JavaScript behaviors that don't all carry into Flutter's webview. Getting those flows to work inside the app required identifying every browser capability the payment flow depended on, determining which were unavailable inside the webview (Android is more restrictive than a desktop browser), and engineering specific workarounds for each one.
- 06
A state management pattern that traded ongoing friction for code reusability
The project used BLoC for state management, chosen by the lead developer to increase code reusability across screens. It delivered on that goal: logic that would otherwise be duplicated was written once and shared. The honest retrospective is more nuanced. BLoC's explicit event-state model requires developers to understand the pattern before they can contribute effectively, and debugging requires tracing events through the BLoC layer rather than following simpler state flows. The team would make that call differently today on a project of this scope — not because BLoC was wrong, but because the reusability benefit didn't clearly outweigh the friction it introduced over the project's lifetime.
outcomes
What we achieved
Concurrences had no native mobile presence. Lawyers and delegates accessed their conferences through a mobile website with no push notifications, no in-app networking, and no offline capability.
The app launched on schedule and has continued running without requiring a full rebuild. Periodic maintenance runs happen roughly every six months. Both platforms are still live.
Every post-launch issue the team handled traced to a change on the client's PHP backend or website — not to the Flutter app itself. The mobile layer has been stable since launch.
Building a mobile layer on top of infrastructure your team already owns?
the build
What we built
The Concurrences Events app is designed to sit on top of infrastructure the client controls — not to replace it. Every architectural decision accounts for the fact that RaftLabs owned the mobile layer and nothing else.
Real-time attendee chat, decoupled from the client's PHP backend
The app's chat layer runs entirely on Firebase, independent of the PHP backend the client's team operated. Building chat on infrastructure RaftLabs couldn't modify independently would have introduced coordination risk every time a chat feature needed to evolve. Firebase gave full control over the chat data model, latency, and scaling without requiring a single change to the client's backend. Push notifications for new messages route through the same Firebase infrastructure.
Sessions that carry a user's identity from native screens into website-backed sections
Several sections of the app open pages on the Concurrences website inside a webview — account management, certain registration flows. Users authenticated in the native app arrive at those pages already logged in. The mechanism is tokenized URLs: when the native app opens a website-backed section, it generates a token encoding the user's authenticated identity and passes it as a URL parameter. The Concurrences website validates the token and establishes the session. The user's experience is seamless; the implementation is explicit and auditable.
Offline caching built in-house for full control over what persists and when it expires
Delegates need to revisit event details, speaker bios, and session schedules without a guaranteed connection. The team built the offline caching layer in-house rather than using an external package. The extra upfront investment bought complete control over what was cached, how long it persisted, and what happened when underlying content changed. For content that updates on a schedule — conference agendas, speaker profiles — that granularity matters.
A Twitter dependency identified and removed before it appeared on a client invoice
The original app embedded live event tweets through the Twitter API. When Twitter's ownership and pricing structure changed, that integration became unsustainable. The team identified the cost before it appeared on an invoice, removed the native API integration, and replaced it with an in-app webview linking to the relevant Twitter account. The feature's intent was preserved. The ongoing API cost was eliminated. No lingering dependency remained.
stack
Why we chose this stack
- 01
Flutter
Concurrences needed iOS and Android from a single build within a 16-week window. Flutter was the right call: one codebase, platform-native performance, and built-in webview support for the sections that needed to open Concurrences website pages inside the app. The client's PHP backend was never touched — Flutter made it straightforward to build the mobile layer as a consumer of the existing API without any backend modifications. One architectural note worth disclosing: the project used BLoC for state management to improve code reusability across screens. It worked as intended, but introduced more ongoing friction than a simpler pattern would have. The team would make that call differently today on a project of this scope.
- 02
Firebase
The app needed real-time attendee chat and push notifications without modifying the client's PHP backend — infrastructure RaftLabs didn't own and couldn't change independently. Firebase solved both: Firestore for real-time messaging, FCM for push notifications, all running as a separate data layer the team controlled entirely. When the chat model needed to evolve, it changed in Firebase without a backend deployment. That independence was the point.
Common questions about this engagement
Yes — and the Concurrences engagement is a direct example. RaftLabs built the entire iOS and Android app as a consumer of the client's existing PHP backend and website, without adding endpoints, modifying the data model, or changing any client-owned infrastructure. The client's developer continued running the backend throughout and after the engagement. Where we needed capabilities the existing backend couldn't provide — real-time chat, push notifications — we added a Firebase layer that ran independently. The mobile layer is fully contained; the client's infrastructure is unchanged.
The Concurrences engagement surfaces a direct example. The original app included a live-tweets section embedded via the Twitter API. When Twitter changed its ownership and pricing structure, that integration became prohibitively expensive to run. The right response was not to absorb the cost silently or wait for the client to notice it on an invoice — it was to identify the dependency, make the cost transparent, and propose a clean replacement. We removed the native integration and replaced it with an in-app webview, preserving the feature without the ongoing API cost. The general principle: when a third-party dependency changes underneath a feature you built responsibly, the goal is a clean exit before the cost becomes recurring.
One codebase for both platforms cuts the build time and team size required to hit a defined timeline. Flutter produces genuinely native-feeling apps on both platforms — not a web wrapper — with the performance and webview integration the Concurrences app required. The trade-off is that some platform-specific behaviors, particularly webview edge cases on Android, required more targeted engineering than they would in a native build. For a project where shipping both platforms within 16 weeks was the priority, Flutter was the right call.
The Concurrences website's payment infrastructure was designed for a full desktop browser — standard HTML forms, browser-level payment APIs, and JavaScript behaviors that don't all carry into Flutter's webview. Getting those flows to work required identifying every browser capability the payment flow depended on, determining which were unavailable inside the webview (Android is more restrictive than a desktop browser), and engineering specific workarounds for each. The approach is not to rebuild the payment system — it is to understand exactly where the webview diverges from a full browser and close those gaps explicitly.
It means every issue the team handled after launch was traced to a change on the Concurrences website or PHP backend — not to the Flutter app itself. The app was never the source of a bug that broke something for a user. That distinction matters for maintenance planning: it tells you where to focus attention when something goes wrong. In the Concurrences engagement, that attention has consistently pointed to the client's infrastructure, not the app. The mobile layer has been stable since initial release.
Related work
More of our work
PSi reaches consensus 75% faster for 300+ users in real-time anonymous voice discussions
Real-time anonymous voice platform for group decision-making. 300+ simultaneous users, 75% faster consensus.
Read case studyTuneClub logs 200+ practice sessions in 60 days
Flutter music learning app connecting digital practice to live performance for Irish musicians.
Read case studyEventRaft reaches 50,000 users in 6 months
Native iOS and Android event platform consolidating registration, scheduling, and attendee networking.
Read case study







