Selecciona la plataforma: Android iOS JavaScript

Componente Place Details

El componente Place Details del kit de IU de Places te permite agregar un componente de IU individual que muestra detalles de lugares en tu app. Este componente es personalizable.

Componente compacto de detalles del lugar

El componente Place Details se puede usar de forma independiente o en conjunto con otras APIs y servicios de Google Maps Platform. El componente toma un ID de lugar, un nombre de recurso o coordenadas de latitud y longitud, y muestra información renderizada de Places Details.

El componente Place Details es totalmente compatible con temas, lo que te permite personalizar fuentes, colores y radios de esquinas para que coincidan con tu caso de uso y los lineamientos de la marca visual. Puedes personalizar la apariencia de los detalles del lugar creando un tema que extienda PlacesMaterialTheme y proporcione anulaciones para los atributos del tema. También puedes personalizar qué campos de detalles del lugar se incluyen. Para ello, especifica una lista de entradas de Content, cada una de las cuales corresponde a un fragmento de información que se muestra sobre el lugar.

Variantes de diseño

El componente Place Details admite dos variantes de diseño principales:

  • Compacto: Es un diseño para obtener una vista previa de la información clave.
  • Completa: Un diseño integral que muestra todos los detalles disponibles del lugar.

El diseño compacto se puede mostrar en orientación vertical u horizontal. Esto te permite integrar el componente en varios diseños y tamaños de pantalla. El diseño completo solo se puede mostrar verticalmente.

Diseños horizontales y verticales
Diseños horizontales y verticales

El componente Place Details te brinda un control detallado sobre el contenido que se muestra en el componente. Cada elemento (como fotos, opiniones e información de contacto) se puede mostrar u ocultar de forma individual, lo que permite una personalización precisa de la apariencia de los componentes y la densidad de la información.

Opciones de contenido de los detalles del lugar
Opciones de visualización de contenido

Vista compacta de Place Details

El fragmento compacto de Place Details (PlaceDetailsCompactFragment) renderiza detalles de un lugar seleccionado con un espacio mínimo. Esto puede ser útil en una ventana de información que destaca un lugar en un mapa, en una experiencia de redes sociales, como compartir una ubicación en un chat, como sugerencia para seleccionar tu ubicación actual o dentro de un artículo de medios para hacer referencia al lugar en Google Maps.

Vista completa de Place Details

La vista completa de los detalles del lugar (PlaceDetailsFragment) ofrece una superficie más grande para mostrar la información de los detalles del lugar y te permite mostrar más tipos de información.

Opciones de visualización de contenido

Puedes especificar qué contenido mostrar con las enumeraciones en PlaceDetailsCompactFragment.Content o PlaceDetailsFragment.Content.

Vista compacta Vista completa
  • Foto del lugar
  • Dirección del lugar
  • Calificación y recuento de calificaciones
  • Tipo de lugar
  • Precio
  • Información sobre la entrada accesible
  • Estado de abierto ahora
  • Collage de fotos del lugar
  • Dirección del lugar
  • Calificación y recuento de calificaciones
  • Tipo de lugar
  • Precio
  • Información de accesibilidad
  • Estado de abierto ahora
  • Horario de atención
  • Resumen editorial
  • Sitio web
  • Número de teléfono
  • Opiniones renderizadas en una pestaña dedicada
  • Plus Code
  • Lista de funciones, renderizada en una pestaña dedicada
  • Destacados específicos del tipo, como los precios de la gasolina en las gasolineras

Facturación

Cuando usas el kit de IU de Place Details, se te factura cada vez que se llama a los métodos .loadWithPlaceId(), .loadWithResourceName() o loadWithCoordinates(). Si cargas el mismo lugar varias veces, se te facturará por cada solicitud.

Para evitar que se te cobre varias veces, no agregues directamente .loadWithPlaceId() o .loadWithResourceName() en los métodos del ciclo de vida de Android. Por ejemplo, no llames directamente a .loadWithPlaceId() o .loadWithResourceName() en el método onResume().

Agrega detalles de lugares a tu app

Puedes agregar detalles de lugares a tu app agregando un fragmento a un diseño. Cuando creas una instancia del fragmento, puedes personalizar la apariencia de la información de detalles del lugar para que se adapte a tus necesidades y coincida con la apariencia de tu app. Más información sobre la personalización

Tienes tres métodos disponibles en Kotlin y Java: uno para cargar el fragmento con un ID de lugar (loadWithPlaceId()), uno para cargar el fragmento con un nombre de recurso (loadWithResourceName()) y uno para cargar el fragmento con coordenadas de latitud y longitud (loadWithCoordinates()). Puedes elegir cualquier método o varios.

La posición predeterminada de la vista compacta es vertical. Si deseas un diseño horizontal, especifica Orientation.HORIZONTAL. También puedes especificar Orientation.VERTICAL de forma opcional para mayor claridad. La vista completa solo se puede mostrar verticalmente.

Consulta los ejemplos en la sección Ejemplos del componente Place Details.

Personaliza la apariencia visual

Personalización visual de los detalles del lugar
Ejemplos de personalización visual

El kit de IU de Places ofrece un enfoque de sistema de diseño para la personalización visual basado aproximadamente en Material Design (con algunas modificaciones específicas de Google Maps). Consulta la referencia de Color y Tipografía de Material Design. De forma predeterminada, el diseño se ajusta al lenguaje de diseño visual de Google Maps.

Opciones de personalización de Place Details

Cuando creas una instancia de un fragmento, puedes especificar un tema que anule cualquiera de los atributos de diseño predeterminados. Los atributos de tema que no se anulan usan los diseños predeterminados. Si deseas admitir un tema oscuro, puedes agregar una entrada para el color en values-night/colors.xml.

  <style name="CustomizedPlaceDetailsTheme" parent="PlacesMaterialTheme">
    <item name="placesColorPrimary">@color/app_primary_color</item>
    <item name="placesColorOnSurface">@color/app_color_on_surface</item>
    <item name="placesColorOnSurfaceVariant">@color/app_color_on_surface</item>
  
    <item name="placesTextAppearanceBodySmall">@style/app_text_appearence_small</item>
  
    <item name="placesCornerRadius">20dp</item>
  </style>

Puedes personalizar los siguientes estilos:

Personalización del color y la tipografía del diálogo
Personalización del color y la tipografía del diálogo
Atributo de tema Uso
Color
placesColorSurface Fondo del contenedor y del diálogo
placesColorOutlineDecorative Borde del contenedor
placesColorPrimary Vínculos, indicador de carga, íconos de descripción general
placesColorOnSurface Encabezados y contenido de diálogos
placesColorOnSurfaceVariant Información del lugar
placesColorSecondaryContainer Fondo del botón
placesColorOnSecondaryContainer Ícono y texto del botón
placesColorNeutralContainer Insignia de revisión de fecha, formas de marcador de posición de carga
placesColorOnNeutralContainer Fecha de revisión, error de carga
placesColorPositiveContainer Insignia de cargador para VE disponible
placesColorOnPositiveContainer Contenido de la insignia de cargador de VE disponible
placesColorPositive Etiqueta de lugar "Abierto ahora"
placesColorNegative Etiqueta de lugar "Cerrado"
placesColorInfo Ícono de entrada accesible
placesColorButtonBorder Botones Abrir en Maps y Aceptar
   
Tipografía
placesTextAppearanceBodySmall Información del lugar
placesTextAppearanceBodyMedium Información del lugar, contenido del diálogo
placesTextAppearanceLabelMedium Contenido de la insignia
placesTextAppearanceLabelLarge Contenido del botón
placesTextAppearanceHeadlineMedium Encabezados de diálogo
placesTextAppearanceDisplaySmall Nombre del lugar
placesTextAppearanceTitleSmall Nombre del lugar
   
Espacios
placesSpacingExtraSmall
placesSpacingSmall
placesSpacingMedium
placesSpacingLarge
placesSpacingExtraLarge
placesSpacingTwoExtraLarge
   
Medición
placesBorderWidth Contenedor
placesBorderWidthButton
   
Forma
placesCornerRadius Contenedor
placesCornerRadiusButton Botones Abrir en Maps y Aceptar (no incluye el botón de ícono redondo)
placesCornerRadiusThumbnail Imagen en miniatura del lugar
placesCornerRadiusCollageOuter Collage de medios
placesCornerRadiusCard Tarjeta de lugar, tarjeta de opinión del usuario
placesCornerRadiusDialog Diálogo de divulgación de Google Maps
   
Atribución de marca de Google Maps
placesColorAttributionLightTheme Botón de divulgación y atribución de Google Maps con tema claro (enums para blanco, gris y negro)
placesColorAttributionDarkTheme Botón de atribución y divulgación de Google Maps con tema oscuro (enums para blanco, gris y negro)

Consulta los ejemplos en la sección Ejemplos del componente Place Details.

Personalización de ancho y altura

Vistas compactas

Anchos recomendados:

  • Orientación vertical: Entre 180 dp y 300 dp
  • Orientación horizontal: Entre 180 dp y 500 dp

Es posible que los anchos inferiores a 160 dp no se muestren correctamente.

La práctica recomendada es no establecer una altura para las vistas compactas. Esto permitirá que el contenido de la ventana establezca la altura, lo que permitirá que se muestre toda la información.

Vistas completas

Para las vistas completas, el ancho recomendado es de entre 250 dp y 450 dp. Es posible que un ancho inferior a 250 dp no se muestre correctamente.

Puedes establecer la altura del componente: la vista vertical de Place Details se desplazará verticalmente dentro del espacio asignado.

La práctica recomendada es establecer una altura para las vistas completas. Esto permitirá que el contenido de la ventana se desplace correctamente.

Colores de atribución

atribución
Attribution

Las condiciones del servicio de Google Maps requieren que uses uno de los tres colores de la marca para la atribución de Google Maps. Esta atribución debe ser visible y accesible cuando se realicen cambios en la personalización.

Ofrecemos 3 colores de la marca para elegir que se pueden configurar de forma independiente para los temas claros y oscuros:

  • Tema claro: placesColorAttributionLight con valores de enumeración para blanco, gris y negro.
  • Tema oscuro: placesColorAttributionDark con valores de enumeración para blanco, gris y negro.

Ejemplos del componente Place Details

Cómo crear una vista compacta o completa

Kotlin

              
        // We create a new instance of the fragment using its factory method.
        // We can specify which content to show, the orientation, and a custom theme.
        val fragment = PlaceDetailsCompactFragment.newInstance(
            PlaceDetailsCompactFragment.ALL_CONTENT, // Show all available content.
            orientation,
            R.style.CustomizedPlaceDetailsTheme,
        ).apply {
            // The PlaceLoadListener provides callbacks for when the place data is successfully
            // loaded or when an error occurs. This is where we update our UI state.
            setPlaceLoadListener(object : PlaceLoadListener {
                override fun onSuccess(place: Place) {
                    Log.d(TAG, &quot;Place loaded: ${place.id}&quot;)
                    // Once the data is loaded, we hide the loading indicator and show the fragment.
                    binding.loadingIndicatorMain.visibility = View.GONE
                    binding.placeDetailsContainer.visibility = View.VISIBLE
                    binding.dismissButton.visibility = View.VISIBLE
                }

                override fun onFailure(e: Exception) {
                    Log.e(TAG, &quot;Place failed to load&quot;, e)
                    // On failure, we hide the UI and notify the user.
                    dismissPlaceDetails()
                    Toast.makeText(this@MainActivity, &quot;Failed to load place details.&quot;, Toast.LENGTH_SHORT).show()
                }
            })
        }

        // We add the fragment to our layout&#39;s container view.
        // `commitNow()` is used to ensure the fragment is immediately added and available,
        // which is important because we need to call a method on it right after.
        supportFragmentManager
            .beginTransaction()
            .replace(binding.placeDetailsContainer.id, fragment)
            .commitNow()

        // **This is the key step**: After adding the fragment, we call `loadWithPlaceId`
        // to trigger the data loading process for the selected place.
        // We use `post` to ensure this runs after the layout has been measured,
        // which can prevent potential timing issues.
        binding.root.post {
            fragment.loadWithPlaceId(placeId)
        }
    }

Java

      
PlaceDetailsCompactFragment fragment =
  PlaceDetailsCompactFragment.newInstance(
        Orientation.HORIZONTAL,
        Arrays.asList(Content.ADDRESS, Content.TYPE, Content.RATING, Content.ACCESSIBLE_ENTRANCE_ICON),
        R.style.CustomizedPlaceDetailsTheme);
    
fragment.setPlaceLoadListener(
  new PlaceLoadListener() {
        @Override public void onSuccess(Place place) { ... }
    
        @Override public void onFailure(Exception e) { ... }
});
    
getSupportFragmentManager()
      .beginTransaction()
      .add(R.id.fragment_container, fragment)
      .commitNow();
    
// Load the fragment with a Place ID.
fragment.loadWithPlaceId(placeId);
      
// Load the fragment with a resource name.
fragment.loadWithResourceName(resourceName);

En esta muestra de código completa, se determina la orientación de la vista compacta de forma programática según la configuración del dispositivo del usuario.

Kotlin

        
package com.example.placedetailsuikit

import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.location.Location
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.lifecycle.ViewModel
import com.example.placedetailsuikit.databinding.ActivityMainBinding
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.PointOfInterest
import com.google.android.libraries.places.api.Places
import com.google.android.libraries.places.api.model.Place
import com.google.android.libraries.places.widget.PlaceDetailsCompactFragment
import com.google.android.libraries.places.widget.PlaceLoadListener
import com.google.android.libraries.places.widget.model.Orientation

private const val TAG = &quot;PlacesUiKit&quot;

/**
 * A simple ViewModel to store UI state that needs to survive configuration changes.
 * In this case, it holds the ID of the selected place. Using a ViewModel is good practice
 * as it prevents data loss during events like screen rotation, ensuring a
 * seamless user experience.
 */
class MainViewModel : ViewModel() {
    var selectedPlaceId: String? = null
}

/**
 * This activity serves as a basic example of integrating the Place Details UI Kit.
 * It demonstrates the fundamental steps required:
 * 1. Setting up a Google Map.
 * 2. Requesting location permissions to center the map.
 * 3. Handling clicks on Points of Interest (POIs) to get a Place ID.
 * 4. Using the Place ID to load and display place details in a [PlaceDetailsCompactFragment].
 */
class MainActivity : AppCompatActivity(), OnMapReadyCallback, GoogleMap.OnPoiClickListener {
    // ViewBinding provides type-safe access to views defined in the XML layout,
    // eliminating the need for `findViewById` and preventing null pointer exceptions.
    private lateinit var binding: ActivityMainBinding
    private var googleMap: GoogleMap? = null

    // The FusedLocationProviderClient is the main entry point for interacting with the
    // fused location provider, which intelligently manages the underlying location technologies.
    private lateinit var fusedLocationClient: FusedLocationProviderClient

    // Using registerForActivityResult is the modern, recommended approach for handling
    // permission requests. It decouples the request from the handling logic, making the
    // code cleaner and easier to manage compared to the older `onRequestPermissionsResult` callback.
    private lateinit var requestPermissionLauncher: ActivityResultLauncher&lt;Array&lt;String&gt;&gt;

    // The `by viewModels()` delegate provides a lazy-initialized ViewModel scoped to this Activity.
    // This ensures that we get the same ViewModel instance across configuration changes.
    private val viewModel: MainViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // The ActivityResultLauncher is initialized here. The lambda defines the callback
        // that will be executed once the user responds to the permission dialog.
        requestPermissionLauncher =
            registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -&gt;
                // We check if either fine or coarse location permission was granted.
                if (permissions[Manifest.permission.ACCESS_FINE_LOCATION] == true || permissions[Manifest.permission.ACCESS_COARSE_LOCATION] == true) {
                    Log.d(TAG, &quot;Location permission granted by user.&quot;)
                    fetchLastLocation()
                } else {
                    // If permission is denied, we inform the user and default to a known location.
                    // This ensures the app remains functional even without location access.
                    Log.d(TAG, &quot;Location permission denied by user.&quot;)
                    Toast.makeText(
                        this,
                        &quot;Location permission denied. Showing default location.&quot;,
                        Toast.LENGTH_LONG
                    ).show()
                    moveToSydney()
                }
            }

        // enableEdgeToEdge() allows the app to draw behind the system bars for a more immersive experience.
        enableEdgeToEdge()
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.dismissButton.setOnClickListener {
            dismissPlaceDetails()
        }

        // --- Crucial: Initialize Places SDK ---
        // It&#39;s essential to initialize the Places SDK before making any other Places API calls.
        // This should ideally be done once, for example, in the Application&#39;s `onCreate`.
        val apiKey = BuildConfig.PLACES_API_KEY
        if (apiKey.isEmpty() || apiKey == &quot;YOUR_API_KEY&quot;) {
            // A valid API key is required for the Places SDK to function.
            Log.e(TAG, &quot;No api key&quot;)
            Toast.makeText(
                this,
                &quot;Add your own API_KEY in local.properties&quot;,
                Toast.LENGTH_LONG
            ).show()
            finish()
            return
        }

        // `initializeWithNewPlacesApiEnabled` is used to opt-in to the new SDK version.
        Places.initializeWithNewPlacesApiEnabled(applicationContext, apiKey)

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
        // ------------------------------------

        // The SupportMapFragment is the container for the map. `getMapAsync` allows us to
        // work with the GoogleMap object via a callback once it&#39;s fully initialized.
        val mapFragment =
            supportFragmentManager.findFragmentById(R.id.map_fragment) as SupportMapFragment?
        mapFragment?.getMapAsync(this)

        // This block handles restoration after a configuration change (e.g., screen rotation).
        // If a place was selected before the rotation, its ID is stored in the ViewModel.
        // We use this ID to immediately show the details fragment again.
        if (viewModel.selectedPlaceId != null) {
            viewModel.selectedPlaceId?.let { placeId -&gt;
                Log.d(TAG, &quot;Restoring PlaceDetailsFragment for place ID: $placeId&quot;)
                showPlaceDetailsFragment(placeId)
            }
        }
    }

    /**
     * This callback is triggered when the GoogleMap object is ready to be used.
     * All map setup logic should be placed here.
     */
    override fun onMapReady(map: GoogleMap) {
        Log.d(TAG, &quot;Map is ready&quot;)
        googleMap = map
        // Setting the OnPoiClickListener allows us to capture user taps on points of interest.
        googleMap?.setOnPoiClickListener(this)

        // After the map is ready, we determine the initial camera position based on location permissions.
        if (isLocationPermissionGranted()) {
            fetchLastLocation()
        } else {
            requestLocationPermissions()
        }
    }

    /**
     * A helper function to centralize the check for location permissions.
     */
    private fun isLocationPermissionGranted(): Boolean {
        return ActivityCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) == PackageManager.PERMISSION_GRANTED ||
                ActivityCompat.checkSelfPermission(
                    this,
                    Manifest.permission.ACCESS_COARSE_LOCATION
                ) == PackageManager.PERMISSION_GRANTED
    }

    /**
     * This function triggers the permission request flow. The result is handled by the
     * ActivityResultLauncher defined in `onCreate`.
     */
    private fun requestLocationPermissions() {
        Log.d(TAG, &quot;Requesting location permissions.&quot;)
        requestPermissionLauncher.launch(
            arrayOf(
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION
            )
        )
    }

    /**
     * Fetches the device&#39;s last known location. This is a fast and battery-efficient way
     * to get a location fix. It should only be called after verifying permissions.
     */
    @SuppressLint(&quot;MissingPermission&quot;)
    private fun fetchLastLocation() {
        // Double-checking permissions here is a good practice, although the call sites are already guarded.
        if (isLocationPermissionGranted()) {
            fusedLocationClient.lastLocation
                .addOnSuccessListener { location: Location? -&gt;
                    if (location != null) {
                        val userLocation = LatLng(location.latitude, location.longitude)
                        googleMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(userLocation, 13f))
                        Log.d(TAG, &quot;Moved to user&#39;s last known location.&quot;)
                    } else {
                        // `lastLocation` can be null if the location has never been recorded.
                        // In this case, we fall back to a default location.
                        Log.d(TAG, &quot;Last known location is null. Falling back to Sydney.&quot;)
                        moveToSydney()
                    }
                }
                .addOnFailureListener {
                    // This listener handles errors in the location fetching process.
                    Log.e(TAG, &quot;Failed to get location.&quot;, it)
                    moveToSydney()
                }
        }
    }

    /**
     * Moves the map camera to a default, hardcoded location (Sydney).
     * This serves as a reliable fallback.
     */
    private fun moveToSydney() {
        val sydney = LatLng(-33.8688, 151.2093)
        googleMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney, 13f))
        Log.d(TAG, &quot;Moved to Sydney&quot;)
    }

    /**
     * This is the callback for the `OnPoiClickListener`. It&#39;s triggered when a user
     * taps a POI on the map.
     */
    override fun onPoiClick(poi: PointOfInterest) {
        val placeId = poi.placeId
        Log.d(TAG, &quot;Place ID: $placeId&quot;)

        // We save the selected place ID to the ViewModel. This is critical for surviving
        // configuration changes. If the user rotates the screen now, the `onCreate`
        // method will be able to restore the place details view.
        viewModel.selectedPlaceId = placeId
        showPlaceDetailsFragment(placeId)
    }

    /**
     * This function is the core of the integration. It creates, configures, and displays
     * the [PlaceDetailsCompactFragment].
     * @param placeId The unique identifier for the place to be displayed.
     */
    private fun showPlaceDetailsFragment(placeId: String) {
        Log.d(TAG, &quot;Showing PlaceDetailsFragment for place ID: $placeId&quot;)

        // We manage the visibility of UI elements to provide feedback to the user.
        // The wrapper is shown, and a loading indicator is displayed while the data is fetched.
        binding.placeDetailsWrapper.visibility = View.VISIBLE
        binding.dismissButton.visibility = View.GONE
        binding.placeDetailsContainer.visibility = View.GONE
        binding.loadingIndicatorMain.visibility = View.VISIBLE

        // The Place Details widget can be displayed vertically or horizontally.
        // We dynamically choose the orientation based on the device&#39;s current configuration.
        val orientation =
            if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
                Orientation.HORIZONTAL
            } else {
                Orientation.VERTICAL
            }

        
        // We create a new instance of the fragment using its factory method.
        // We can specify which content to show, the orientation, and a custom theme.
        val fragment = PlaceDetailsCompactFragment.newInstance(
            PlaceDetailsCompactFragment.ALL_CONTENT, // Show all available content.
            orientation,
            R.style.CustomizedPlaceDetailsTheme,
        ).apply {
            // The PlaceLoadListener provides callbacks for when the place data is successfully
            // loaded or when an error occurs. This is where we update our UI state.
            setPlaceLoadListener(object : PlaceLoadListener {
                override fun onSuccess(place: Place) {
                    Log.d(TAG, &quot;Place loaded: ${place.id}&quot;)
                    // Once the data is loaded, we hide the loading indicator and show the fragment.
                    binding.loadingIndicatorMain.visibility = View.GONE
                    binding.placeDetailsContainer.visibility = View.VISIBLE
                    binding.dismissButton.visibility = View.VISIBLE
                }

                override fun onFailure(e: Exception) {
                    Log.e(TAG, &quot;Place failed to load&quot;, e)
                    // On failure, we hide the UI and notify the user.
                    dismissPlaceDetails()
                    Toast.makeText(this@MainActivity, &quot;Failed to load place details.&quot;, Toast.LENGTH_SHORT).show()
                }
            })
        }

        // We add the fragment to our layout&#39;s container view.
        // `commitNow()` is used to ensure the fragment is immediately added and available,
        // which is important because we need to call a method on it right after.
        supportFragmentManager
            .beginTransaction()
            .replace(binding.placeDetailsContainer.id, fragment)
            .commitNow()

        // **This is the key step**: After adding the fragment, we call `loadWithPlaceId`
        // to trigger the data loading process for the selected place.
        // We use `post` to ensure this runs after the layout has been measured,
        // which can prevent potential timing issues.
        binding.root.post {
            fragment.loadWithPlaceId(placeId)
        }
    }


    /**
     * Hides the place details view and clears the selected place ID from the ViewModel.
     */
    private fun dismissPlaceDetails() {
        binding.placeDetailsWrapper.visibility = View.GONE
        // Clearing the ID in the ViewModel is important so that if the user rotates the
        // screen after dismissing, the details view doesn&#39;t reappear.
        viewModel.selectedPlaceId = null
    }

    override fun onDestroy() {
        super.onDestroy()
        // It&#39;s a good practice to nullify references to objects that have a lifecycle
        // tied to the activity, like the GoogleMap object, to prevent potential memory leaks.
        googleMap = null
    }
}
        
  
Sugerencia: Accede a las muestras de código completas en GitHub.

Cómo crear un tema

Cuando creas una instancia de un fragmento, puedes especificar un tema que anule cualquiera de los atributos de diseño predeterminados. Los atributos de tema que no se anulan usan los diseños predeterminados. Si deseas admitir un tema oscuro, puedes agregar una entrada para el color en values-night/colors.xml.

  <style name="CustomizedPlaceDetailsTheme" parent="PlacesMaterialTheme">
    <item name="placesColorPrimary">@color/app_primary_color</item>
    <item name="placesColorOnSurface">@color/app_color_on_surface</item>
    <item name="placesColorOnSurfaceVariant">@color/app_color_on_surface</item>
  
    <item name="placesTextAppearanceBodySmall">@style/app_text_appearence_small</item>
  
    <item name="placesCornerRadius">20dp</item>
  </style>

Usa contenido estándar

En este ejemplo, se usa el contenido estándar.

  val fragmentStandardContent = PlaceDetailsCompactFragment.newInstance(
    PlaceDetailsCompactFragment.STANDARD_CONTENT,
    orientation,
    R.style.CustomizedPlaceDetailsTheme
  )

Personaliza contenido específico

En este ejemplo, se seleccionan solo las opciones de dirección, entrada accesible y medios Content para una vista compacta, y se renderizan con el CustomizedPlaceDetailsTheme.

  val placeDetailsFragment = PlaceDetailsCompactFragment.newInstance(
    orientation,
    listOf(
        Content.ADDRESS,
        Content.ACCESSIBLE_ENTRANCE,
        Content.MEDIA
    ),
    R.style.CustomizedPlaceDetailsTheme
)

Usar todo el contenido

En este ejemplo, se usan todas las opciones de Content de una vista compacta.

  val fragmentAllContent = PlaceDetailsCompactFragment.newInstance(
    orientation,
    PlaceDetailsCompactFragment.ALL_CONTENT,
    R.style.CustomizedPlaceDetailsTheme
  )