generateItinerariesFromEntries static method

Future<List<Map<String, dynamic>>> generateItinerariesFromEntries(
  1. List<DiaryEntry> entries
)

Generates 5 suggested itineraries based on a list of DiaryEntry objects.

The function sends the entries to the Gemini API, prompting it to return five imaginative travel itineraries inspired by the diary entries, each composed of a list of Google Maps Marker objects. The generated itineraries suggest new locations and are not limited to those found in the original entries.

Returns a Future containing a list of 5 itineraries, where each itinerary is a List of Markers.

Throws:

  • SocketException if there is no internet connection.
  • FormatException if the response from the AI model cannot be decoded as JSON.
  • Exception if the response is empty or if any other unexpected error occurs during parsing.

Implementation

static Future<List<Map<String, dynamic>>> generateItinerariesFromEntries(
  List<DiaryEntry> entries,
) async {
  // Build entry summaries
  String entrySummaries = entries
      .map((entry) {
        return '''
Titolo: ${entry.title}
Data: ${entry.date.toIso8601String()}
Descrizione: ${entry.description}
Luogo: lat=${entry.latitude}, lon=${entry.longitude}
''';
      })
      .join('\n\n');

  // Updated schema with 'title' for each itinerary
  final jsonSchema = Schema.object(
    properties: {
      'itineraries': Schema.array(
        items: Schema.object(
          properties: {
            'title': Schema.string(),
            'stops': Schema.array(
              items: Schema.object(
                properties: {
                  'name': Schema.string(),
                  'latitude': Schema.number(),
                  'longitude': Schema.number(),
                },
              ),
            ),
          },
        ),
      ),
    },
  );

  // Updated prompt
  final prompt = TextPart('''
Ti fornisco un elenco di voci di diario di viaggio (titolo, descrizione, luogo).
Crea **5 diversi itinerari turistici** ispirati a questi racconti, proponendo nuove località **simili o collegate** per atmosfera, tema o interesse.

Per ogni itinerario fornisci:
- **title**: un titolo suggestivo per l’itinerario, in inglese
- **stops**: un array di 5 tappe, ognuna con:
- "name": il nome del luogo, in inglese
- "latitude": la latitudine
- "longitude": la longitudine

Le tappe devono essere **geograficamente vicine** (massimo 200 km l’una dall’altra).

Ecco le voci di diario da cui prendere ispirazione:

$entrySummaries
''');

  final model = FirebaseAI.googleAI().generativeModel(
    model: 'gemini-2.0-flash-001',
    generationConfig: GenerationConfig(
      responseMimeType: 'application/json',
      responseSchema: jsonSchema,
    ),
  );

  try {
    final response = await model.generateContent([Content.text(prompt.text)]);

    if (response.text == null) {
      throw Exception('Empty response.');
    }

    final rawText = response.text!;
    final jsonResponse = jsonDecode(rawText);
    final itineraries = jsonResponse['itineraries'] as List;

    return itineraries.map<Map<String, dynamic>>((itinerary) {
      final title = itinerary['title'] as String;
      final stops =
          (itinerary['stops'] as List).map<Marker>((stop) {
            return Marker(
              markerId: MarkerId(stop['name']),
              position: LatLng(
                (stop['latitude'] as num).toDouble(),
                (stop['longitude'] as num).toDouble(),
              ),
              infoWindow: InfoWindow(title: stop['name']),
            );
          }).toList();
      return {'title': title, 'markers': stops};
    }).toList();
  } on SocketException {
    throw Exception('No connection. Could not generate new itineraries.');
  } catch (e) {
    throw Exception('Error parsing response: ${e.toString()}');
  }
}