Uygulamanız için gezinmeyi tasarlarken koşullu mantık doğrultusunda bir hedefe gitmek yerine diğerine gitmek isteyebilirsiniz. Örneğin, bir kullanıcı, giriş yapmasını gerektiren bir hedefe giden derin bağlantıyı izleyebilir veya bir oyunda oyuncunun kazandığı ya da kaybettiği durumlar için farklı hedefleriniz olabilir.
Kullanıcı girişi
Bu örnekte, bir kullanıcı kimlik doğrulama gerektiren bir profil ekranına gitmeyi dener. Bu işlem kimlik doğrulama gerektirdiğinden, kullanıcı kimliği önceden doğrulanmamışsa giriş ekranına yönlendirilmelidir.
Bu örnekteki gezinme grafiği aşağıdaki gibi görünebilir:
Kimlik doğrulama için uygulamanın login_fragment
sayfasına gitmesi gerekir. Burada kullanıcı, kimliği doğrulamak için kullanıcı adı ve şifre girmelidir. Kabul edilirse kullanıcı tekrar profile_fragment
ekranına gönderilir. Kabul edilmezse kullanıcıya bir Snackbar
kullanılarak kimlik bilgilerinin geçersiz olduğu bildirilir.
Kullanıcı, giriş yapmadan profil ekranına geri dönerse main_fragment
ekranına yönlendirilir.
Bu uygulamanın gezinme grafiği şöyledir:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/main_fragment">
<fragment
android:id="@+id/main_fragment"
android:name="com.google.android.conditionalnav.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main">
<action
android:id="@+id/navigate_to_profile_fragment"
app:destination="@id/profile_fragment"/>
</fragment>
<fragment
android:id="@+id/login_fragment"
android:name="com.google.android.conditionalnav.LoginFragment"
android:label="login_fragment"
tools:layout="@layout/login_fragment"/>
<fragment
android:id="@+id/profile_fragment"
android:name="com.google.android.conditionalnav.ProfileFragment"
android:label="fragment_profile"
tools:layout="@layout/fragment_profile"/>
</navigation>
MainFragment
, kullanıcının profilini görüntülemek için tıklayabileceği bir düğme içerir.
Kullanıcı, profil ekranını görmek istiyorsa önce kimliğini doğrulaması gerekir. Bu etkileşim iki ayrı parça kullanılarak modellenir ancak paylaşılan kullanıcı durumuna bağlıdır. Bu durum bilgisi, bu iki parçanın herhangi birinin sorumluluğunda değildir ve paylaşılan bir UserViewModel
içinde daha uygun bir şekilde tutulur.
Bu ViewModel
, ViewModelStoreOwner
işlemini uygulayan etkinliğe göre kapsama alınarak parçalar arasında paylaşılır. Aşağıdaki örnekte, MainActivity
ProfileFragment
barındırdığı için requireActivity()
, MainActivity
olarak çözümlenir:
Kotlin
class ProfileFragment : Fragment() { private val userViewModel: UserViewModel by activityViewModels() ... }
Java
public class ProfileFragment extends Fragment { private UserViewModel userViewModel; @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); userViewModel = new ViewModelProvider(requireActivity()).get(UserViewModel.class); ... } ... }
UserViewModel
içindeki kullanıcı verileri LiveData
üzerinden açığa çıkarılır. Bu nedenle, nereye gideceğinize karar vermek için bu verileri gözlemlemeniz gerekir. ProfileFragment
uygulamasına gittiğinde, kullanıcı verileri mevcutsa uygulama bir karşılama mesajı gösterir. Kullanıcı verileri null
ise kullanıcının profilini görmeden önce kimlik doğrulaması yapması gerektiğinden LoginFragment
sayfasına gidersiniz. Karar verme mantığını ProfileFragment
öğenizde aşağıdaki örnekte gösterildiği gibi tanımlayın:
Kotlin
class ProfileFragment : Fragment() { private val userViewModel: UserViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val navController = findNavController() userViewModel.user.observe(viewLifecycleOwner, Observer { user -> if (user != null) { showWelcomeMessage() } else { navController.navigate(R.id.login_fragment) } }) } private fun showWelcomeMessage() { ... } }
Java
public class ProfileFragment extends Fragment { private UserViewModel userViewModel; @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); userViewModel = new ViewModelProvider(requireActivity()).get(UserViewModel.class); final NavController navController = Navigation.findNavController(view); userViewModel.user.observe(getViewLifecycleOwner(), (Observer<User>) user -> { if (user != null) { showWelcomeMessage(); } else { navController.navigate(R.id.login_fragment); } }); } private void showWelcomeMessage() { ... } }
ProfileFragment
hedefine ulaşan kullanıcı verileri null
ise LoginFragment
öğesine yönlendirilir.
Önceki hedefin NavBackStackEntry
durumunu almak için NavController.getPreviousBackStackEntry()
kullanabilirsiniz. Bu değer, hedef için NavController
'a özgü durumu içerir. LoginFragment
, kullanıcının başarıyla giriş yapıp yapmadığını gösteren bir başlangıç değeri ayarlamak için önceki NavBackStackEntry
öğesinin SavedStateHandle
değerini kullanır. Kullanıcı hemen sistem geri düğmesine basarsa, geri dönmek isteyeceğimiz durum budur. Bu durumu SavedStateHandle
kullanarak ayarlamak, işlemin tamamlanabilmesini sağlayarak bu durumun devam etmesini sağlar.
Kotlin
class LoginFragment : Fragment() { companion object { const val LOGIN_SUCCESSFUL: String = "LOGIN_SUCCESSFUL" } private val userViewModel: UserViewModel by activityViewModels() private lateinit var savedStateHandle: SavedStateHandle override fun onViewCreated(view: View, savedInstanceState: Bundle?) { savedStateHandle = findNavController().previousBackStackEntry!!.savedStateHandle savedStateHandle.set(LOGIN_SUCCESSFUL, false) } }
Java
public class LoginFragment extends Fragment { public static String LOGIN_SUCCESSFUL = "LOGIN_SUCCESSFUL" private UserViewModel userViewModel; private SavedStateHandle savedStateHandle; @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { userViewModel = new ViewModelProvider(requireActivity()).get(UserViewModel.class); savedStateHandle = Navigation.findNavController(view) .getPreviousBackStackEntry() .getSavedStateHandle(); savedStateHandle.set(LOGIN_SUCCESSFUL, false); } }
Kullanıcı, kullanıcı adı ve şifre girdikten sonra kimlik doğrulama için UserViewModel
aracına iletilir. Kimlik doğrulama başarılı olursa UserViewModel
, kullanıcı verilerini depolar. LoginFragment
daha sonra SavedStateHandle
üzerindeki LOGIN_SUCCESSFUL
değerini günceller ve kendini arka yığından çıkarır.
Kotlin
class LoginFragment : Fragment() { companion object { const val LOGIN_SUCCESSFUL: String = "LOGIN_SUCCESSFUL" } private val userViewModel: UserViewModel by activityViewModels() private lateinit var savedStateHandle: SavedStateHandle override fun onViewCreated(view: View, savedInstanceState: Bundle?) { savedStateHandle = findNavController().previousBackStackEntry!!.savedStateHandle savedStateHandle.set(LOGIN_SUCCESSFUL, false) val usernameEditText = view.findViewById(R.id.username_edit_text) val passwordEditText = view.findViewById(R.id.password_edit_text) val loginButton = view.findViewById(R.id.login_button) loginButton.setOnClickListener { val username = usernameEditText.text.toString() val password = passwordEditText.text.toString() login(username, password) } } fun login(username: String, password: String) { userViewModel.login(username, password).observe(viewLifecycleOwner, Observer { result -> if (result.success) { savedStateHandle.set(LOGIN_SUCCESSFUL, true) findNavController().popBackStack() } else { showErrorMessage() } }) } fun showErrorMessage() { // Display a snackbar error message } }
Java
public class LoginFragment extends Fragment { public static String LOGIN_SUCCESSFUL = "LOGIN_SUCCESSFUL" private UserViewModel userViewModel; private SavedStateHandle savedStateHandle; @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { userViewModel = new ViewModelProvider(requireActivity()).get(UserViewModel.class); savedStateHandle = Navigation.findNavController(view) .getPreviousBackStackEntry() .getSavedStateHandle(); savedStateHandle.set(LOGIN_SUCCESSFUL, false); EditText usernameEditText = view.findViewById(R.id.username_edit_text); EditText passwordEditText = view.findViewById(R.id.password_edit_text); Button loginButton = view.findViewById(R.id.login_button); loginButton.setOnClickListener(v -> { String username = usernameEditText.getText().toString(); String password = passwordEditText.getText().toString(); login(username, password); }); } private void login(String username, String password) { userViewModel.login(username, password).observe(viewLifecycleOwner, (Observer<LoginResult>) result -> { if (result.success) { savedStateHandle.set(LOGIN_SUCCESSFUL, true); NavHostFragment.findNavController(this).popBackStack(); } else { showErrorMessage(); } }); } private void showErrorMessage() { // Display a snackbar error message } }
Kimlik doğrulama ile ilgili tüm mantığın UserViewModel
kapsamında tutulduğunu unutmayın. Kullanıcıların kimliklerinin nasıl doğrulanacağını belirlemek LoginFragment
veya ProfileFragment
sorumluluğu olmadığından bu önemlidir. Mantığınızı bir ViewModel
içine almanız, yalnızca paylaşmayı ve test etmeyi de kolaylaştırır. Gezinme mantığınız karmaşıksa bu mantığı özellikle test ederek doğrulamanız gerekir. Uygulamanızın mimarisini test edilebilir bileşenlere göre yapılandırma hakkında daha fazla bilgi için Uygulama mimarisi rehberine bakın.
ProfileFragment
içinde, SavedStateHandle
içinde depolanan LOGIN_SUCCESSFUL
değeri onCreate()
yönteminde gözlemlenebilir. Kullanıcı ProfileFragment
öğesine döndüğünde LOGIN_SUCCESSFUL
değeri kontrol edilir. Değer false
ise kullanıcı tekrar MainFragment
uygulamasına yönlendirilebilir.
Kotlin
class ProfileFragment : Fragment() { ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val navController = findNavController() val currentBackStackEntry = navController.currentBackStackEntry!! val savedStateHandle = currentBackStackEntry.savedStateHandle savedStateHandle.getLiveData<Boolean>(LoginFragment.LOGIN_SUCCESSFUL) .observe(currentBackStackEntry, Observer { success -> if (!success) { val startDestination = navController.graph.startDestination val navOptions = NavOptions.Builder() .setPopUpTo(startDestination, true) .build() navController.navigate(startDestination, null, navOptions) } }) } ... }
Java
public class ProfileFragment extends Fragment { ... @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); NavController navController = NavHostFragment.findNavController(this); NavBackStackEntry navBackStackEntry = navController.getCurrentBackStackEntry(); SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle(); savedStateHandle.getLiveData(LoginFragment.LOGIN_SUCCESSFUL) .observe(navBackStackEntry, (Observer<Boolean>) success -> { if (!success) { int startDestination = navController.getGraph().getStartDestination(); NavOptions navOptions = new NavOptions.Builder() .setPopUpTo(startDestination, true) .build(); navController.navigate(startDestination, null, navOptions); } }); } ... }
Kullanıcı başarıyla giriş yaparsa ProfileFragment
bir karşılama mesajı gösterir.
Sonucu kontrol etmek için burada kullanılan teknik, iki farklı durumu birbirinden ayırt etmenizi sağlar:
- Kullanıcının giriş yapmadığı ve giriş yapması istenen ilk durum.
- Kullanıcı, giriş yapmamayı seçtiğinden (
false
nedeniyle) giriş yapmamıştır.
Bu kullanım alanlarını birbirinden ayırt ederek sürekli olarak kullanıcıdan giriş yapmasını istemekten kaçınabilirsiniz. Hata durumlarını ele almaya ilişkin iş mantığı size bırakılır ve önceki kod örneğinde olduğu gibi, kullanıcının neden giriş yapması gerektiğini, tüm etkinliği tamamlamasını veya kullanıcıyı giriş gerektirmeyen bir hedefe yönlendirmeyi açıklayan bir yer paylaşımı görüntüleyebilir.