Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Integrate a Travel Itinerary Planner in FlutterFlow

Build a travel itinerary planner in FlutterFlow by integrating Google Places API for activity search, flight and hotel APIs (Amadeus or Skyscanner) via Cloud Functions, and storing the user's trip plan in Firestore. Users search a destination, browse flights and accommodations, add activities to day-by-day itineraries, and see all stops on an interactive Google Map. Debounce search inputs to avoid burning through Places API quota on every keystroke.

What you'll learn

  • How to integrate Google Places API for destination and activity search with debounced input
  • How to connect to a flight search API via Cloud Function and display results in FlutterFlow
  • How to build a day-by-day itinerary data structure in Firestore and bind it to a tabbed UI
  • How to display itinerary activities as markers on a FlutterFlowGoogleMap
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner9 min read90-120 minFlutterFlow Free+March 2026RapidDev Engineering Team
TL;DR

Build a travel itinerary planner in FlutterFlow by integrating Google Places API for activity search, flight and hotel APIs (Amadeus or Skyscanner) via Cloud Functions, and storing the user's trip plan in Firestore. Users search a destination, browse flights and accommodations, add activities to day-by-day itineraries, and see all stops on an interactive Google Map. Debounce search inputs to avoid burning through Places API quota on every keystroke.

Combine Google Places, flight APIs, and Firestore to build a full travel planner

A travel itinerary planner brings together several external data sources: destination search and point-of-interest data from Google Places, flight availability from a flight search API, hotel listings from an accommodation API, and the user's saved trip plan stored in Firestore. FlutterFlow's API Manager handles the Google Places calls directly. Flight and hotel APIs require Cloud Function proxies because they use server-side credentials. The trip plan itself — a collection of days each containing an ordered list of activities — lives in Firestore and powers the day-by-day itinerary UI with a map showing all stops.

Prerequisites

  • A FlutterFlow project with Firebase configured (Settings → Project Setup → Firebase)
  • Google Cloud project with Places API and Maps SDK enabled — get an API key from Google Cloud Console
  • An Amadeus API account (test keys available free at developers.amadeus.com) or Skyscanner affiliate account
  • Cloud Functions enabled on Firebase (Blaze plan required)

Step-by-step guide

1

Set up Google Places API for destination and activity search

In FlutterFlow's API Manager, create an API Group named GooglePlaces with base URL https://maps.googleapis.com/maps/api/place. Add your Places API key to FlutterFlow Secrets (Settings → Secrets → PLACES_API_KEY). Add a first API Call named searchPlaces with method GET, path /textsearch/json, query parameters query (Variable) and key ([PLACES_API_KEY]). Add a second API Call named getPlaceDetails with method GET, path /details/json, query parameters place_id (Variable), fields (text: name,rating,formatted_address,photos,geometry,opening_hours,website), and key. Mark response fields: for searchPlaces mark results[].name, results[].place_id, results[].rating, results[].photos[0].photo_reference, results[].geometry.location.lat, results[].geometry.location.lng. For getPlaceDetails mark result.name, result.formatted_address, result.website, result.opening_hours.open_now.

Expected result: You can search for destinations and points of interest, receiving names, ratings, photos, and coordinates. Place details load on demand when a user taps a result.

2

Build the Cloud Function for flight search

Create a Cloud Function named searchFlights. It accepts origin, destination, departureDate, and returnDate as query parameters. The function calls the Amadeus Flight Offers Search API: POST https://test.api.amadeus.com/v2/shopping/flight-offers with an OAuth Bearer token obtained from Amadeus token endpoint first. Map the response to a simplified array of flight objects: airline, flightNumber, departureTime, arrivalTime, duration, stops, price, currency. Store your Amadeus client_id and client_secret in Cloud Function environment variables. Return the simplified flight array as JSON. In FlutterFlow's API Manager, add an API Group named FlightSearch pointing to your Cloud Function URL and add a call named getFlights with the required parameters.

Expected result: The searchFlights Cloud Function returns a list of available flights for the given route and dates in a clean, consistent format ready for FlutterFlow data binding.

3

Design the Firestore trip data structure

Create a trips Firestore collection with documents: userId, destination (name and placeId), startDate, endDate, coverImageUrl, createdAt, status (planning / booked / completed). Under each trip, create a days subcollection: dayNumber (Integer), date (Timestamp). Under each day, create an activities subcollection: placeId, name, address, lat, lng, startTime, endTime, category (flight / hotel / activity / restaurant / transport), notes, orderIndex. In FlutterFlow, add these collections in the Firestore panel with appropriate field types. This nested structure lets you query all activities for a specific day efficiently without loading the entire trip.

Expected result: The Firestore schema supports multi-day trips with ordered activities per day, ready for the itinerary UI components to be built against it.

4

Build the day-by-day itinerary UI with tabs

Create a TripDetail page with a Page Parameter tripId (String). Add a Backend Query loading the trip document. Create a TabBar where each tab corresponds to a day of the trip — generate tabs dynamically by calculating the number of days between startDate and endDate. For each tab, add a Backend Query loading the activities subcollection for that day ordered by orderIndex. Display activities in a ReorderableListView (or standard ListView with up/down buttons to adjust orderIndex). Each activity row shows a category icon, name, time range, and a Delete button. Add an Add Activity button at the bottom of each day's tab that opens a Bottom Sheet with a Places search to find and add new activities.

Expected result: Users see a tabbed view with one tab per day. Each day shows its activities in order. Activities can be reordered, deleted, and new ones added via place search.

5

Display itinerary activities on a Google Map

Create a TripMap page that loads all activities across all days for the trip. Add a FlutterFlowGoogleMap widget. Use a Custom Function to build a list of MapMarker objects from the activities — each marker gets the activity's lat/lng, a color based on day number (day 1 = blue, day 2 = green, day 3 = orange), and a label showing the activity name. Set the map's initial camera to the destination's coordinates (stored on the trip document). Add a marker tap handler that opens a Bottom Sheet showing the activity details: name, address, category, and a button to open directions in Google Maps via Launch URL to https://maps.google.com/?q={lat},{lng}.

Expected result: All activities across the trip appear as color-coded markers on the map. Tapping a marker shows activity details and offers a directions link.

Complete working example

search_flights_function.js
1const functions = require('firebase-functions');
2const axios = require('axios');
3
4const CLIENT_ID = functions.config().amadeus.client_id;
5const CLIENT_SECRET = functions.config().amadeus.client_secret;
6
7async function getAmadeusToken() {
8 const res = await axios.post(
9 'https://test.api.amadeus.com/v1/security/oauth2/token',
10 new URLSearchParams({
11 grant_type: 'client_credentials',
12 client_id: CLIENT_ID,
13 client_secret: CLIENT_SECRET,
14 }),
15 { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
16 );
17 return res.data.access_token;
18}
19
20exports.searchFlights = functions.https.onRequest(async (req, res) => {
21 res.set('Access-Control-Allow-Origin', '*');
22 if (req.method === 'OPTIONS') return res.status(204).send('');
23
24 const { origin, destination, departureDate, returnDate } = req.query;
25 if (!origin || !destination || !departureDate) {
26 return res.status(400).json({ error: 'origin, destination, departureDate required' });
27 }
28
29 try {
30 const token = await getAmadeusToken();
31 const params = {
32 originLocationCode: origin,
33 destinationLocationCode: destination,
34 departureDate,
35 adults: 1,
36 max: 10,
37 currencyCode: 'USD',
38 };
39 if (returnDate) params.returnDate = returnDate;
40
41 const { data } = await axios.get(
42 'https://test.api.amadeus.com/v2/shopping/flight-offers',
43 { headers: { Authorization: `Bearer ${token}` }, params }
44 );
45
46 const flights = (data.data || []).slice(0, 10).map((offer) => {
47 const seg = offer.itineraries[0].segments[0];
48 return {
49 airline: seg.carrierCode,
50 flightNumber: seg.carrierCode + seg.number,
51 departureTime: seg.departure.at,
52 arrivalTime: seg.arrival.at,
53 duration: offer.itineraries[0].duration,
54 stops: offer.itineraries[0].segments.length - 1,
55 price: offer.price.total,
56 currency: offer.price.currency,
57 };
58 });
59 res.json({ flights });
60 } catch (err) {
61 res.status(500).json({ error: err.message });
62 }
63});

Common mistakes

Why it's a problem: Calling Google Places API on every keystroke in the destination search field

How to avoid: Add a 300ms debounce using a Timer Custom Action and require at least 3 characters before triggering the API call. This reduces calls by ~80% with no perceptible impact on user experience.

Why it's a problem: Storing all trip activities in a flat array on the trip document instead of subcollections

How to avoid: Use the nested structure: trips/{id}/days/{dayId}/activities/{activityId}. This lets you load only the specific day you're viewing and scales to any number of activities.

Why it's a problem: Embedding the Google Places API key directly in the FlutterFlow app

How to avoid: Store the key in FlutterFlow Secrets (Settings → Secrets) which encrypts it and keeps it out of source code. Add API key restrictions in Google Cloud Console to limit its use to your app's bundle ID.

Best practices

  • Cache Google Places details in Firestore after the first lookup — place data rarely changes, so storing it avoids repeat API calls for popular destinations
  • Use Amadeus test keys during development and only switch to production keys before launch — test environment uses simulated data and does not incur real charges
  • Add an offline mode that shows the stored itinerary from Firestore even without connectivity — travelers often lack data in foreign countries
  • Include a share feature using share_plus to send the trip itinerary as a deep link or PDF so users can share plans with travel companions
  • Generate a photo reference URL for Places photos: https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photoreference={ref}&key={key} — cache the resulting image URLs in Firestore to avoid regenerating them
  • Add currency conversion to display flight and hotel prices in the user's local currency using an exchange rate API
  • Test the complete flow on a physical device in airplane mode after initial data load to verify the offline itinerary experience

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I am building a travel itinerary planner in FlutterFlow. Write a Firebase Cloud Function that: (1) accepts origin airport code, destination airport code, and departure date as query parameters, (2) authenticates with the Amadeus API using client credentials OAuth flow, (3) calls the Amadeus Flight Offers Search v2 endpoint, and (4) returns a simplified list of up to 10 flights with airline, flight number, departure time, arrival time, duration, number of stops, price, and currency. Include error handling and CORS headers.

FlutterFlow Prompt

Create a trip detail page in my FlutterFlow app that shows a day-by-day tabbed itinerary for a trip loaded from Firestore. Each tab shows one day's activities in a ListView ordered by orderIndex. Each activity row shows a category icon, name, time range, and address. Add a floating action button to open a bottom sheet for adding new activities via a Google Places search.

Frequently asked questions

Which flight search API should I use for a FlutterFlow travel app?

Amadeus is the best choice for development — free test keys, comprehensive API documentation, and real IATA data. For production, Amadeus charges per search. Skyscanner requires partnership approval but has lower fees. Aviasales and Kiwi.com are alternatives with affiliate commission models. All require Cloud Function proxies since client credentials must stay server-side.

How do I display Google Places photos in FlutterFlow?

Google Places returns a photo_reference string, not a direct image URL. Build the image URL in a Custom Function: return 'https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photoreference=' + photoRef + '&key=' + apiKey. Bind this URL to your Image widget's Network Image property. Cache the URL in Firestore to avoid regenerating it on every load.

Can users share their trip itinerary with other users in the app?

Yes. Add a collaborators array to the trip document containing user IDs. Update Firestore security rules to allow read/write access if request.auth.uid is in resource.data.collaborators. Add a Share Trip button that looks up the collaborator by email and adds their UID to the array. The trip then appears in both users' trip lists.

How do I handle the Google Places API billing to avoid unexpected charges?

Set a budget alert in Google Cloud Console at your comfortable monthly limit. Enable the Places API key restrictions to only allow calls from your app's bundle ID (iOS) or SHA-1 fingerprint (Android). Implement the debounce described in this tutorial. Cache place results in Firestore to eliminate repeat API calls for the same destination.

Can I add real hotel booking (not just search) to the travel planner?

Real hotel booking requires integration with a booking engine that handles payments and reservations — Booking.com affiliate API, Expedia Partner Solutions, or direct hotel chain APIs. These are complex partnerships with legal agreements. For an MVP, show search results and link to the booking platform's website using Launch URL. Implement in-app booking only after establishing the affiliate partnership.

What if I need help integrating multiple travel APIs and designing the Firestore schema?

Travel apps with multiple API integrations (flights, hotels, activities, maps) and complex Firestore schemas benefit significantly from experienced FlutterFlow architecture. RapidDev has built production travel apps in FlutterFlow and can design the full data model and API integration layer.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.