Navigation 2'den Navigation 3'e geçiş yapma

Uygulamanızı Navigation 2'den Navigation 3'e taşımak için aşağıdaki adımları uygulayın:

  1. Navigation 3 bağımlılıklarını ekleyin.
  2. NavKey arayüzünü uygulamak için navigasyon rotalarınızı güncelleyin.
  3. Navigasyon durumunuzu korumak ve değiştirmek için sınıflar oluşturun.
  4. NavController yerine bu sınıfları kullanın.
  5. Hedeflerinizi NavHost adlı kullanıcının NavGraph klasöründen bir entryProvider klasörüne taşıyın.
  6. NavHost yerine NavDisplay koyun.
  7. Navigation 2 bağımlılıklarını kaldırın.

Hazırlık

Aşağıdaki bölümlerde, taşıma işlemi için ön koşullar ve projenizle ilgili varsayımlar açıklanmaktadır. Ayrıca, taşıma için desteklenen ve desteklenmeyen özellikler de ele alınır.

Ön koşullar

  • compileSdk 36 veya daha yeni bir sürüm kullanmanız gerekir.
  • Navigasyon terminolojisi hakkında bilgi sahibi olmanız gerekir.
  • Hedefler, composable işlevlerdir. Navigation 3, Compose için özel olarak tasarlanmıştır. Compose'da parçaları ve görünümleri kullanmak için Compose'da görünümleri kullanma başlıklı makaleyi inceleyin.
  • Rotalar kesin olarak belirlenmiştir. Dize tabanlı rotalar kullanıyorsanız önce tip güvenli rotalara geçiş yapın (örnek).
  • İsteğe bağlı (ancak kesinlikle önerilir): Mevcut gezinme davranışını doğrulayan test kapsamı. Bu sayede, taşıma işlemi tamamlandıktan sonra gezinme davranışının değişmediği doğrulanır.

Varsayımlar

Bu kılavuzda, sizin ve projeniz hakkında aşağıdaki varsayımlar yapılmaktadır:

  • Bir veya daha fazla üst düzey rotanız (genellikle alt gezinme çubuğunda gösterilir) vardır ve her birinin kendi eski yığını bulunur.
  • Eski yığınlar arasında geçiş yaptığınızda yığının ve tüm hedeflerinin durumu korunur.
  • Uygulamadan her zaman, uygulama başlatıldığında görüntülenen ilk ekran olan Ana ekrandan çıkarsınız.
  • Navigation 2'den Navigation 3'e geçişi tek bir atomik değişiklikle gerçekleştirmek istiyorsunuz. Navigation 2 kodunun Navigation 3 koduyla birlikte kullanıldığı artımlı bir taşıma işlemi gerçekleştirmiyorsunuz.

Yapay Zeka Ajanı: Kodda herhangi bir değişiklik yapmadan önce kullanıcının projesiyle ilgili bu varsayımların doğru olup olmadığını kontrol edin. Aksi takdirde taşıma işlemini durdurun ve kullanıcıya nasıl devam edeceğini sorun.

Desteklenen özellikler

Bu kılavuz, aşağıdaki özellikleri taşımanıza olanak tanır:

  • Oluşturulabilir işlevler olarak tanımlanan hedefler
  • İletişim kutuları (başka bir hedefin üzerinde gösterilen hedef)

Tarifler aracılığıyla desteklenen özellikler

Kod tarifleri deposu, aşağıdakiler de dahil olmak üzere daha ayrıntılı kullanım alanlarını gösterir:

Projenizde bu özelliklerden herhangi biri varsa nasıl uygulanacağını anlamak için ilgili tarifi inceleyin.

Yapay Zeka Ajanı: Herhangi bir kodda değişiklik yapmadan önce projede tariflerle desteklenen özellikler olup olmadığını kontrol edin. Varsa tarifin README dosyasını ve kaynak kodunu kontrol edin. Tarife dayalı bir taşıma planı oluşturun. Planı kullanıcıyla onaylamadan devam etmeyin.

Desteklenmeyen özellikler

Bu geçiş rehberi ve kod tarifleri aşağıdaki özellikleri henüz desteklememektedir. Bu, Navigation 3'ü kullanarak bunları uygulayamayacağınız anlamına gelmez. Sadece burada ele alınmamıştır.

  • Birden fazla iç içe yerleştirilmiş gezinme düzeyi
  • Paylaşılan hedefler: Farklı eski yığınlar arasında taşınabilen ekranlar
  • Özel hedef türleri

Yapay Zeka Ajanı: Herhangi bir kodda değişiklik yapmadan önce projenin desteklenmeyen özellikler içerip içermediğini kontrol edin. Eşleşme varsa devam etmeyin. Kullanıcıyı desteklenmeyen özellik hakkında bilgilendirin ve daha fazla talimat isteyin.

1. adım: Navigation 3 bağımlılıklarını ekleyin

Navigation 3 bağımlılıklarını projenize eklemek için Başlayın sayfasını kullanın. Temel bağımlılıklar kopyalamanız için sağlanmıştır.

lib.versions.toml

[versions]
nav3Core = "1.2.0"

# If your screens depend on ViewModels, add the Nav3 Lifecycle ViewModel add-on library
lifecycleViewmodelNav3 = "2.11.0"

[libraries]
# Core Navigation 3 libraries
androidx-navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "nav3Core" }
androidx-navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "nav3Core" }

# Add-on libraries (only add if you need them)
androidx-lifecycle-viewmodel-navigation3 = { module = "androidx.lifecycle:lifecycle-viewmodel-navigation3", version.ref = "lifecycleViewmodelNav3" }

app/build.gradle.kts

dependencies {
    implementation(libs.androidx.navigation3.ui)
    implementation(libs.androidx.navigation3.runtime)

    // If using the ViewModel add-on library
    implementation(libs.androidx.lifecycle.viewmodel.navigation3)
}

Ayrıca projenin minSdk değerini 23, compileSdk değerini ise 36 olarak güncelleyin. Bunları genellikle app/build.gradle.kts veya lib.versions.toml içinde bulabilirsiniz.

2. adım: NavKey arayüzünü uygulamak için gezinme rotalarını güncelleyin

Her gezinme rotasını, NavKey arayüzünü uygulayacak şekilde güncelleyin. Bu sayede, gezinme durumunuzu kaydetmenize yardımcı olması için rememberNavBackStack uygulamasını kullanabilirsiniz.

Önce:

@Serializable data object RouteA

Sonra:

@Serializable data object RouteA : NavKey

3. adım: Gezinme durumunuzu tutmak ve değiştirmek için sınıflar oluşturun

3.1. adım: Gezinme durumu bilgisi depolayıcı oluşturma

Aşağıdaki kodu NavigationState.kt adlı bir dosyaya kopyalayın. Proje yapınıza uygun bir paket adı ekleyin.

// package com.example.project

import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSerializable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.runtime.toMutableStateList
import androidx.navigation3.runtime.NavBackStack
import androidx.navigation3.runtime.NavEntry
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.rememberDecoratedNavEntries
import androidx.navigation3.runtime.rememberNavBackStack
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
import androidx.navigation3.runtime.serialization.NavKeySerializer
import androidx.savedstate.compose.serialization.serializers.MutableStateSerializer

/**
 * Create a navigation state that persists config changes and process death.
 */
@Composable
fun rememberNavigationState(
    startRoute: NavKey,
    topLevelRoutes: Set<NavKey>
): NavigationState {

    val topLevelRoute = rememberSerializable(
        startRoute, topLevelRoutes,
        serializer = MutableStateSerializer(NavKeySerializer())
    ) {
        mutableStateOf(startRoute)
    }

    val backStacks = topLevelRoutes.associateWith { key -> rememberNavBackStack(key) }

    return remember(startRoute, topLevelRoutes) {
        NavigationState(
            startRoute = startRoute,
            topLevelRoute = topLevelRoute,
            backStacks = backStacks
        )
    }
}

/**
 * State holder for navigation state.
 *
 * @param startRoute - the start route. The user will exit the app through this route.
 * @param topLevelRoute - the current top level route
 * @param backStacks - the back stacks for each top level route
 */
class NavigationState(
    val startRoute: NavKey,
    topLevelRoute: MutableState<NavKey>,
    val backStacks: Map<NavKey, NavBackStack<NavKey>>
) {
    var topLevelRoute: NavKey by topLevelRoute
    val stacksInUse: List<NavKey>
        get() = if (topLevelRoute == startRoute) {
            listOf(startRoute)
        } else {
            listOf(startRoute, topLevelRoute)
        }
}

/**
 * Convert NavigationState into NavEntries.
 */
@Composable
fun NavigationState.toEntries(
    entryProvider: (NavKey) -> NavEntry<NavKey>
): SnapshotStateList<NavEntry<NavKey>> {

    val decoratedEntries = backStacks.mapValues { (_, stack) ->
        val decorators = listOf(
            rememberSaveableStateHolderNavEntryDecorator<NavKey>(),
        )
        rememberDecoratedNavEntries(
            backStack = stack,
            entryDecorators = decorators,
            entryProvider = entryProvider
        )
    }

    return stacksInUse
        .flatMap { decoratedEntries[it] ?: emptyList() }
        .toMutableStateList()
}

Yapay zeka ajanı: rememberSerializable doğru. rememberSaveable olarak değiştirmeyin.

Bu dosya, NavigationState adlı bir durum bilgisi depolayıcı sınıfı ve ilişkili yardımcı işlevleri içerir. Her birinin kendi eski yığını olan bir dizi üst düzey rota içerir. Dahili olarak, mevcut üst düzey rotayı kalıcı hale getirmek için rememberSerializable (rememberSaveable değil) ve her üst düzey rota için eski yığınları kalıcı hale getirmek için rememberNavBackStack kullanır.

3.2. adım: Etkinliklere yanıt olarak gezinme durumunu değiştiren bir nesne oluşturun

Aşağıdaki kodu Navigator.kt adlı bir dosyaya kopyalayın. Proje yapınızla eşleşecek şekilde paket adınızı ekleyin.

// package com.example.project

import androidx.navigation3.runtime.NavKey

/**
 * Handles navigation events (forward and back) by updating the navigation state.
 */
class Navigator(val state: NavigationState) {
    fun navigate(route: NavKey) {
        if (route in state.backStacks.keys) {
            // This is a top level route, just switch to it.
            state.topLevelRoute = route
        } else {
            state.backStacks[state.topLevelRoute]?.add(route)
        }
    }

    fun goBack() {
        val currentStack = state.backStacks[state.topLevelRoute]
            ?: error("Stack for ${state.topLevelRoute} not found")
        val currentRoute = currentStack.last()

        // If we're at the base of the current route, go back to the start route stack.
        if (currentRoute == state.topLevelRoute) {
            state.topLevelRoute = state.startRoute
        } else {
            currentStack.removeLastOrNull()
        }
    }
}

Navigator sınıfı, iki gezinme etkinliği yöntemi sağlar:

  • Belirli bir rotaya navigate.
  • Mevcut rotadan goBack uzakta.

Her iki yöntem de NavigationState öğesini değiştirir.

3.3. adım: NavigationState ve Navigator oluşturun

NavController ile aynı kapsama sahip NavigationState ve Navigator örnekleri oluşturun.

val navigationState = rememberNavigationState(
    // ...
    startRoute = <Insert your starting route>,
    topLevelRoutes = <Insert your set of top level routes>
    // ...
)

val navigator = remember { Navigator(navigationState) }

4. adım: NavController değiştirin

NavController gezinme etkinliği yöntemlerini Navigator eşdeğerleriyle değiştirin.

NavController alanı veya yöntemi

Navigator eşdeğeri

navigate()

navigate()

popBackStack()

goBack()

previousBackStackEntry.savedStateHandle.set()

ResultEventBus.sendResult()

NavController alanlarını NavigationState alanlarıyla değiştirin.

NavController alanı veya yöntemi

NavigationState eşdeğeri

currentBackStack

backStacks[topLevelRoute]

currentBackStackEntry

currentBackStackEntryAsState()

currentBackStackEntryFlow

currentDestination

backStacks[topLevelRoute].last()

currentBackStackEntry.savedStateHandle.getLiveData()

currentBackStackEntry.savedStateHandle.getStateFlow()

ResultEffect (etkinliğe dayalı)

ResultEventBus.conflateAsState() (eyalet bazlı)

Üst düzey rotayı alın: Bulmak için mevcut eski yığın girişinden hiyerarşide yukarı doğru ilerleyin.

topLevelRoute

Gezinme çubuğunda seçili olan öğeyi belirlemek için NavigationState.topLevelRoute kullanın.

Önce:

// ...
val isSelected = navController.currentBackStackEntryAsState().value?.destination.isRouteInHierarchy(key::class)
// ...

fun NavDestination?.isRouteInHierarchy(route: KClass<*>) =
    this?.hierarchy?.any {
        it.hasRoute(route)
    } ?: false

Sonra:

val isSelected = key == navigationState.topLevelRoute

İçe aktarmalar da dahil olmak üzere NavController ile ilgili tüm referansları kaldırdığınızı doğrulayın.

4.1. adım: Yaşam döngüsüne duyarlı mantığı taşıma

Navigation 2'de NavBackStackEntry, LifecycleOwner'i uygulayarak navController.currentBackStackEntry ile yaşam döngüsü olaylarını dinlemenize veya akışları yaşam döngüsüne duyarlı bir şekilde toplamanıza olanak tanır.

Navigation 3'te NavDisplay, her hedefin composable içeriğine LocalLifecycleOwner.current aracılığıyla giriş kapsamlı bir LifecycleOwner sağlar. Daha fazla bilgi için Hedef yaşam döngüsü bölümünü inceleyin.

Yaşam döngüsüne duyarlı işlemleri, LocalLifecycleOwner.current öğesine referans vererek doğrudan hedefinizin composable içeriğinde gerçekleştirmeniz gerekir.

Örneğin, eski yığın girişini kullanarak yaşam döngüsüne duyarlı bir şekilde akış topluyorsanız:

Önce:

// In your destination screen or host
val lifecycleOwner = navController.currentBackStackEntry!!
val state by flow.collectAsStateWithLifecycle(lifecycleOwner = lifecycleOwner)

Sonra:

// Inside the destination composable
val state by flow.collectAsStateWithLifecycle()

4.2. adım: Taşıma sonucunu iletme

Navigation 2'de hedefler, SavedStateHandle üzerindeki NavBackStackEntry yöntemini kullanarak sonuçları önceki hedeflere geri iletir. SavedStateHandle, kaydedilen örnek durumu tarafından desteklendiğinden döndürülen veriler işlem sonlandırmadan otomatik olarak kurtuldu.

Önce:

// Sender destination:
navController.previousBackStackEntry?.savedStateHandle?.set("contact_key", contact)
navController.popBackStack()

// Receiver destination:
val lifecycleOwner = LocalLifecycleOwner.current
navController.currentBackStackEntry?.savedStateHandle
    ?.getLiveData<Contact>("contact_key")
    ?.observe(lifecycleOwner) { contact ->
        viewModel.onRecipientSelected(contact)
    }

3. gezinme bölümünde NavDisplay.entryDecorators için rememberResultEventBusNavEntryDecorator() ekleyin. Gönderen hedefinizin entryProvider eşlemesinde LocalResultEventBus.current değerini alın ve sendResult() işlevini çağırın. Alıcı hedefte, etkinliği bir ViewModel hedefine yönlendirmek veya yan etki tetiklemek için ResultEffect kullanın.

Sonra:

// Sender destination (in entryProvider):
entry<ContactPickerRoute> {
    val resultBus = LocalResultEventBus.current

    ContactPickerScreen(
        onContactSelected = { contact ->
            resultBus.sendResult<Contact>(result = contact)
            navigator.goBack()
        }
    )
}

// Receiver destination:
@Composable
fun ComposeMessageScreen(viewModel: ComposeMessageViewModel = viewModel()) {
    ResultEffect<Contact> { contact ->
        viewModel.onRecipientSelected(contact)
    }

    ComposeMessageContent(recipient = viewModel.recipient)
}

Duruma dayalı gözlem için resultBus.conflateAsState() yöntemini çağırabilirsiniz. Daha fazla bilgi için Sonuçları döndürme bölümüne bakın.

5. adım: Hedeflerinizi NavHost adlı mülkün NavGraph adlı mülkünden bir entryProvider mülküne taşıyın

Navigation 2'de, genellikle NavHost öğesinin sondaki lambda'sında NavGraphBuilder DSL'yi kullanarak hedeflerinizi tanımlarsınız. Burada, Gezinme kodunuzu kapsülleme bölümünde açıklandığı gibi uzantı işlevlerini kullanmak yaygındır.

Navigation 3'te hedeflerinizi entryProvider kullanarak tanımlarsınız. Bu entryProvider, bir rotayı NavEntry olarak çözümler. Önemli bir nokta olarak, entryProvider, girişler arasında üst-alt ilişkileri tanımlamaz.

Bu geçiş rehberinde, alt-üst ilişkileri aşağıdaki gibi modellenmiştir:

  • NavigationState, bir dizi üst düzey rotaya (üst rotalar) ve her biri için bir yığına sahiptir. Mevcut üst düzey rotayı ve ilişkili yığınını takip eder.
  • Yeni bir rotaya giderken Navigator, rotanın üst düzey rota olup olmadığını kontrol eder. Eşleşiyorsa mevcut üst düzey rota ve yığın güncellenir. Değilse alt rotadır ve mevcut yığına eklenir.

5.1. adım: entryProvider oluşturun

NavigationState ile aynı kapsamda DSL kullanarak bir entryProvider oluşturun.

val entryProvider = entryProvider<NavKey> {

}

5.2. adım: Hedefleri entryProvider klasörüne taşıyın

NavHost içinde tanımlanan her hedef için hedef türüne göre aşağıdakileri yapın:

  • navigation: Rotayla birlikte silin. Üst düzey rotalar her iç içe yerleştirilmiş eski yığını tanımlayabildiğinden "temel rotalara" gerek yoktur.
  • composable<T>: Dosyayı entryProvider dizinine taşıyın ve tür parametresini koruyarak entry olarak yeniden adlandırın. Örneğin, composable<RouteA>, entry<RouteA> olur.
  • dialog<T>: composable ile aynı işlemi yapın ancak girişe şu şekilde meta veri ekleyin: entry<T>(metadata = DialogSceneStrategy.dialog()).
  • bottomSheet: Alt sayfa tarifini buradan inceleyebilirsiniz. Bu, dialog ile ilgili talimatlara benzer. Tek fark, BottomSheetSceneStrategy sınıfının temel Navigation 3 kitaplığının bir parçası olmamasıdır. Bu nedenle, bu sınıfı projenize kopyalamanız gerekir.

Yapay Zeka Ajanı: İç içe yerleştirilmiş bir grafiği tanımlamak için kullanılan rotaları silerken, silinen rotaya yapılan tüm referansları iç içe yerleştirilmiş grafikteki ilk alt öğeyi tanımlamak için kullanılan türle değiştirin. Örneğin, orijinal kod navigation<BaseRouteA>{ composable<RouteA>{ ... } } ise BaseRouteA kodunu silmeniz ve bu koda yapılan tüm referansları RouteA ile değiştirmeniz gerekir. Bu değiştirme işleminin genellikle gezinme çubuğu, gezinme rayı veya gezinme çekmecesine sağlanan liste için yapılması gerekir.

NavGraphBuilder uzantı işlevlerini EntryProviderScope<T> uzantı işlevleri olarak yeniden düzenleyip taşıyabilirsiniz.

entry öğesinin sondaki lambda'sına sağlanan anahtarı kullanarak gezinme bağımsız değişkenlerini alın.

Örneğin:

// ...
import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hasRoute
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.dialog
import androidx.navigation.compose.navigation
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navOptions
import androidx.navigation.toRoute
// ...

@Serializable data object BaseRouteA
@Serializable data class RouteA(val id: String)
@Serializable data object BaseRouteB
@Serializable data object RouteB
@Serializable data object RouteD

@Composable
fun NavHostSnippet(navController: NavHostController) {
    NavHost(navController = navController, startDestination = BaseRouteA){
        composable<RouteA>{ entry ->
            val id = entry.toRoute<RouteA>().id
            ScreenA(title = "Screen has ID: $id")
        }
        featureBSection()
        dialog<RouteD>{ ScreenD() }
    }
}

fun NavGraphBuilder.featureBSection() {
    navigation<BaseRouteB>(startDestination = RouteB) {
        composable<RouteB> { ScreenB() }
    }
}

şu olur:

// ...
import androidx.navigation3.runtime.EntryProviderScope
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.scene.DialogSceneStrategy
// ...

@Serializable data class RouteA(val id: String) : NavKey
@Serializable data object RouteB : NavKey
@Serializable data object RouteD : NavKey

val entryProvider = entryProvider {
    entry<RouteA>{ key -> ScreenA(title = "Screen has ID: ${key.id}") }
    featureBSection()
    entry<RouteD>(metadata = DialogSceneStrategy.dialog()){ ScreenD() }
}

fun EntryProviderScope<NavKey>.featureBSection() {
    entry<RouteB> { ScreenB() }
}

6. adım: NavHost yerine NavDisplay yazın

NavHost yerine NavDisplay koyun.

  • NavHost öğesini silip NavDisplay ile değiştirin.
  • entries = navigationState.toEntries(entryProvider) parametresini belirtin. Bu işlem, gezinme durumunu entryProvider kullanarak NavDisplay tarafından gösterilen girişlere dönüştürür.
  • NavDisplay.onBack cihazını navigator.goBack() cihazına bağlayın. Bu, NavDisplay'in yerleşik geri işleyicisi tamamlandığında navigator öğesinin gezinme durumunu güncellemesine neden olur.
  • İletişim kutusu hedefleriniz varsa NavDisplay'in sceneStrategies parametresine DialogSceneStrategy ekleyin.

Örneğin:

NavDisplay(
    entries = navigationState.toEntries(entryProvider),
    onBack = { navigator.goBack() },
    sceneStrategies = remember { listOf(DialogSceneStrategy()) }
)

7. adım: Derin bağlantıları taşıma

Navigation 2'de derin bağlantılar, hedeflerin deepLinks parametresi kullanılarak doğrudan gezinme grafiğinde tanımlanıyordu.

Navigation 3'te derin bağlantılar, gezinme kullanıcı arayüzünden bağımsız olarak yönetilir. İlk eski yığını oluşturmak için DeepLinkMatcher'leri tanımlar ve etkinliğinizdeki gelen istekleri eşleştirirsiniz.

Önce:

Navigation 2'de derin bağlantıyı şu şekilde tanımlamış olabilirsiniz:

Sonra:

3. gezinme adımında rota için bir UriDeepLinkMatcher tanımlarsınız:

Ardından, etkinliğinizin onCreate (ve onNewIntent) yönteminde gelen intent'i eşleştirip eski yığınınızı başlatırsınız:

Özel bağımsız değişken türleri

Navigation 2'de, özel NavType uygulamaları ve typeMap kullanarak özel veya üçüncü taraf bağımsız değişken türlerini (ör. LocalDateTime) işlediniz.

Navigation 3'te, özel veya üçüncü taraf türlerini URI parametrelerinden seri durumdan çıkarmak için bir DeepLinkSerializer tanımlayın. Ayrıntılar için DeepLinkSerializer ile özel serileştirme başlıklı makaleyi inceleyin.

Sentetik eski yığınlar ve özel eşleştiriciler gibi daha ileri düzey kullanım alanları için Derin bağlantıları destekleme kılavuzuna bakın.

8. adım: Navigation 2 bağımlılıklarını kaldırın

Tüm Navigation 2 içe aktarma işlemlerini ve kitaplık bağımlılıklarını kaldırın.

Özet

Tebrikler! Projeniz Navigation 3'e taşındı. Siz veya yapay zeka temsilciniz bu kılavuzu kullanırken herhangi bir sorunla karşılaştıysanız buradan hata bildiriminde bulunun.