# Criluma Viaggi

> A travel agency in the pocket: its offers, a holiday search, booking and payment, and a meeting with a consultant.

Case study by Paolo Gianfelici (Full-stack developer & UI/UX designer). https://paologianfelici.com/work/criluma-viaggi

- **Role:** Main Flutter developer: payments, sign-in with Google, Facebook and Apple, appointments, push notifications, the null-safety migration and the 2.0 release
- **Client:** Criluma Viaggi, travel agency
- **Period:** 2018–2022 (last release 2.0.1, November 2021)
- **Platforms:** iOS and Android from one Flutter codebase
- **Stack:** Flutter, Dart, BLoC, RxDart, Firebase Auth, Cloud Messaging, Google Sign-In, Facebook Login, Sign in with Apple, Stripe, Nexi, MyBank

Criluma Viaggi is a travel agency in the Marche region, and its app put the agency in the customer's pocket: the offers of the moment, a search over the holidays its booking system sells (flight and hotel, or the hotel alone) and a booking made on the phone, from the quote to the travellers' details, the insurance and the payment.

Customers could also book a meeting with one of the agency's travel consultants, at the agency or on a video call. I was the app's main developer, on iOS and Android from one Flutter codebase, until version 2.0.1 in November 2021.

*The app is no longer on the stores; the agency is still in business. The recordings on this page do not touch its systems: they run the app's last release as a self-contained build, with the booking server, sign-in and the bank answered inside the page. Hotels, offers, prices, consultants and the customer are invented, the pictures are drawn for this portfolio, nothing is paid and nothing was sent anywhere.*

- 183: of the 222 commits in the app's repository are mine
- 18k: lines of Dart in the last release
- 3: payment integrations over the years: Stripe, Nexi and MyBank
- 4: ways to sign in: the agency's account, Google, Facebook and Apple

## From an offer in the window to a paid holiday

### The offers in the window

The home page is the agency's shop window: a carousel of offers, each with the destination, the hotel and its stars, the dates and the price. An offer opens with its pictures, what is included and a form that sends the agency a question about it.

### A search that follows what is on sale

Flight and hotel, the hotel alone, or a free search. Destination, month and departure airport narrow each other: after every choice the server answers with the countries, regions, months and airports that still have departures.

### Hotel, flights and rooms

Results sort by price, category or type. A hotel opens with its pictures, the tour operator, the outbound and return flights and the rooms available for the party; rooms can be added and the flights and dates changed, and the total follows.

### From the quote to the booking

The quote puts the flights, the stay and the insurance on one page. Confirming it asks for each traveller of each room (title, name, phone, date of birth and tax code), then the optional insurance, the terms, and a summary with the consents the contract requires.

### Paying with MyBank

The checkout in the last release's code pays by MyBank transfer: the app opens the agency's payment page, the customer picks their bank, signs in and authorises the transfer there, and comes back to the app with the holiday paid. Earlier versions took cards through Stripe, and I built a Nexi card checkout too.

### A consultant, at the agency or on video

A signed-in customer finds their details already filled in: they pick a day, a time and what the meeting is about, and a consultant to meet at the agency or on a video call. Sign-in uses the agency's CriluMy account, or Google, Facebook and Apple.

## How it was built

### Talking to a travel booking system

Everything comes from the agency's booking server, an ASP.NET web service whose requests wrap their fields in a JSON string inside JSON, and whose answers do the same. The app turns them into typed models and BLoCs on RxDart streams: the search keeps destinations, months and airports in step, and the booking carries the party, rooms, flights and quote from screen to screen.

### Payments, and a plugin

For Nexi's card payments I wrote nexi_payment, a Flutter plugin over Nexi's Android and iOS SDKs, published on pub.dev in June 2020; I still maintain it. In the app, cards through Stripe came first and Nexi next, then MyBank through GestPay (2020–2021), with the app opening the agency's payment page in a web view and reading the outcome from the address it returns to.

### Accounts, and keeping up with Flutter

A CriluMy sign-in is checked by the agency's server and mirrored in Firebase Auth; Google, Facebook and Apple go through Firebase and are matched to the customer's CriluMy account by user name or e-mail, or open one. The project started on a Flutter beta in 2018 and followed Flutter to 2.x: the new Android embedding in 2020, sound null safety for 2.0 in 2021.

## Recording an app that has left the stores: The app is real. The holidays are invented.

The app is off the stores, but the agency is in business, and its booking system, offers and customers are its own. So the recordings use a copy of the last release that runs on its own, with a stand-in for everything behind it.

- **The last release, built for the browser.** Version 2.0.1, built for the web with Flutter 2.10, a few months younger than the app, and told it runs on an iPhone. The search and in-app booking are switched back on as they were in 1.6, with the few null-safety slips in those screens fixed.
- **A booking server inside the page.** Every request the app makes is answered in the page by a stand-in that speaks the web service's format, from an invented catalogue: nine hotels from Sardinia to the Maldives, two tour operators, flights from five airports, rooms and quotes. Sign-in runs on the in-memory Firebase used in tests.
- **Drawn pictures, invented people.** The suppliers' photographs stay out: a script draws every beach, pool and room, the consultants' portraits and the operators' logos. The consultants, the customer and the banks are invented, and the bank's pages are drawn by the demo.
- **Recorded offline.** The recorder refuses any request that leaves the machine, so a take cannot reach the agency's systems.

The logo, the interface and its icons are the app's own. The offers, hotels, prices, flights, consultants, customer, banks and pictures are sample content made for this portfolio.

## Recordings

- **Searching for a holiday** (77 s): A flight and stay search: the destinations narrow from country to region, the months and airports follow what is available, and the results open on the hotel with its flights, rooms and total, then the quote. https://paologianfelici.com/media/criluma/clips/search.mp4?v=1df3ca86d7
- **Booking and paying** (84 s): From the quote to the payment: the travellers' details for each room, the optional insurance and the terms, the summary, and a MyBank transfer authorised at the customer's bank (banks invented, nothing paid). https://paologianfelici.com/media/criluma/clips/checkout.mp4?v=9b766ae93f
- **The agency's offers** (44 s): The home page is a carousel of the agency's offers. One opens with its pictures, the hotel, what is included and the dates, and a form to ask the agency about it. https://paologianfelici.com/media/criluma/clips/offers.mp4?v=72d6364bdd
- **An appointment with a consultant** (39 s): Booking a meeting with one of the agency's travel consultants, at the agency or on video: day, time, the subject and the consultant. https://paologianfelici.com/media/criluma/clips/appointment.mp4?v=332230d9c3
