Kotlin:登录后Activity向Fragment传值返回Null问题求助
问题:Firebase登录后邮箱无法通过Bundle/SafeArgs传递到Fragment
我用Firebase Auth和Firestore实现登录界面,两者通过同一邮箱关联。希望在邮箱登录或注册成功后,将邮箱传递到MainActivity的MainFragment中,用于访问Firestore。尝试用Bundle和SafeArgs传值,但始终返回Null,相关代码如下:
LoginActivity.kt
private val db = Firebase.firestore.collection("users") private val auth = Firebase.auth private lateinit var binding: ActivityLoginBinding private fun loginUser(email: String, password: String) { if (email.isNotEmpty() && password.isNotEmpty()) { CoroutineScope(Dispatchers.IO).launch { try { auth.signInWithEmailAndPassword(email, password) .addOnSuccessListener { val fragment = MainFragment() val bundle = Bundle() bundle.putString("mail", email) fragment.arguments = bundle checkLogged() Toast.makeText(this@LoginActivity, "Welcome Again", Toast.LENGTH_SHORT) .show() } .addOnFailureListener { Toast.makeText(this@LoginActivity, it.message, Toast.LENGTH_LONG).show() }.await() } catch (e: Exception) { withContext(Dispatchers.Main) { Toast.makeText(this@LoginActivity, e.message, Toast.LENGTH_LONG).show() } } } } } private fun registerUser(email: String, password: String, user: User) { if (email.isNotEmpty() && password.isNotEmpty()) { CoroutineScope(Dispatchers.IO).launch { try { auth.createUserWithEmailAndPassword(email, password) .addOnSuccessListener { db.document(user.email).set(user) val fragment = MainFragment() val bundle = Bundle() bundle.putString("email", user.email) fragment.arguments = bundle checkLogged() Toast.makeText( this@LoginActivity, "Welcome ${user.name}", Toast.LENGTH_SHORT ).show() }.await() } catch (e: java.lang.Exception) { withContext(Dispatchers.Main) { Toast.makeText(this@LoginActivity, e.message, Toast.LENGTH_LONG).show() } } } } } private fun checkLogged() { if (Firebase.auth.currentUser != null) { Firebase.auth.currentUser!!.reload() startActivity(Intent(this@LoginActivity, MainActivity::class.java)) finish() } else { // } }
MainFragment.kt
class MainFragment : Fragment() { private val db = Firebase.firestore.collection("users") private lateinit var binding: FragmentMainBinding override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { binding = FragmentMainBinding.inflate(inflater, container, false) if (arguments != null){ val textName = arguments?.getString("email") binding.txtName.text = textName } return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) }
nav_graph.xml
<?xml version="1.0" encoding="utf-8"?> <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/mainFragment"> <fragment android:id="@+id/addMealFragment" android:name="com.meetozan.caloriecounter.AddMealFragment" android:label="fragment_add_meal" tools:layout="@layout/fragment_add_meal" /> <fragment android:id="@+id/calendarFragment" android:name="com.meetozan.caloriecounter.CalendarFragment" android:label="fragment_calendar" tools:layout="@layout/fragment_calendar" /> <fragment android:id="@+id/foodsFragment" android:name="com.meetozan.caloriecounter.FoodsFragment" android:label="fragment_foods" tools:layout="@layout/fragment_foods" /> <fragment android:id="@+id/leaderboardFragment" android:name="com.meetozan.caloriecounter.LeaderboardFragment" android:label="fragment_leaderboard" tools:layout="@layout/fragment_leaderboard" /> <fragment android:id="@+id/mainFragment" android:name="com.meetozan.caloriecounter.MainFragment" android:label="fragment_main" tools:layout="@layout/fragment_main"> <action android:id="@+id/action_mainFragment_to_addMealFragment" app:destination="@id/addMealFragment" /> <action android:id="@+id/action_mainFragment_to_calendarFragment" app:destination="@id/calendarFragment" /> <action android:id="@+id/action_mainFragment_to_foodsFragment" app:destination="@id/foodsFragment" /> <action android:id="@+id/action_mainFragment_to_leaderboardFragment" app:destination="@id/leaderboardFragment" /> <argument android:name="email" app:argType="string" app:nullable="true" /> </fragment> <activity android:id="@+id/loginActivity" android:name="com.meetozan.caloriecounter.LoginActivity" android:label="activity_login" tools:layout="@layout/activity_login" /> </navigation>
解决方案
问题根源
你在LoginActivity里手动创建了MainFragment实例并设置Bundle,但最终启动的是MainActivity——MainActivity中的MainFragment是由导航组件自动初始化的,和你手动创建的实例完全无关,所以参数根本传不到目标Fragment里。
方案1:通过Intent传递邮箱到MainActivity,再在Fragment中获取
- 修改LoginActivity的
checkLogged方法,接收邮箱参数并通过Intent传递:
private fun checkLogged(email: String) { if (Firebase.auth.currentUser != null) { Firebase.auth.currentUser!!.reload() val intent = Intent(this@LoginActivity, MainActivity::class.java) intent.putExtra("user_email", email) startActivity(intent) finish() } }
- 在
loginUser和registerUser的addOnSuccessListener中,调用checkLogged时传入对应邮箱:- loginUser里:
checkLogged(email) - registerUser里:
checkLogged(user.email)
- loginUser里:
- 在MainFragment中从Activity的Intent获取参数:
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { binding = FragmentMainBinding.inflate(inflater, container, false) // 从Activity的Intent中读取邮箱 val email = activity?.intent?.getStringExtra("user_email") email?.let { binding.txtName.text = it // 用该邮箱访问Firestore db.document(it).get().addOnSuccessListener { doc -> // 处理用户数据 } } return binding.root }
方案2:用导航组件SafeArgs直接跳转(符合Jetpack规范)
- 确保项目已启用SafeArgs(在app模块的build.gradle中配置),然后在nav_graph.xml中添加LoginActivity到MainFragment的action:
<activity android:id="@+id/loginActivity" android:name="com.meetozan.caloriecounter.LoginActivity" android:label="activity_login" tools:layout="@layout/activity_login"> <action android:id="@+id/action_loginActivity_to_mainFragment" app:destination="@id/mainFragment"/> </activity>
- 修改LoginActivity的跳转逻辑,替换
checkLogged方法,直接用导航组件跳转并传参:
// 在loginUser的addOnSuccessListener中 val action = LoginActivityDirections.actionLoginActivityToMainFragment(email) findNavController().navigate(action) finish() // 在registerUser的addOnSuccessListener中 val action = LoginActivityDirections.actionLoginActivityToMainFragment(user.email) findNavController().navigate(action) finish()
- 在MainFragment中用SafeArgs获取参数:
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { binding = FragmentMainBinding.inflate(inflater, container, false) val args = MainFragmentArgs.fromBundle(requireArguments()) args.email?.let { binding.txtName.text = it } return binding.root }
方案3:直接从Firebase Auth获取邮箱(最可靠,无需传值)
既然已经集成了Firebase Auth,登录成功后可以直接在MainFragment中获取当前用户的邮箱,完全不需要手动传值:
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { binding = FragmentMainBinding.inflate(inflater, container, false) val currentUser = Firebase.auth.currentUser currentUser?.email?.let { userEmail -> binding.txtName.text = userEmail // 用该邮箱访问Firestore db.document(userEmail).get().addOnSuccessListener { document -> if (document.exists()) { val user = document.toObject(User::class.java) // 处理用户数据 } } } return binding.root }
这个方法避免了传值过程中可能出现的错误,是最推荐的实现方式。
内容的提问来源于stack exchange,提问作者Mert Ozan
相关产品推荐
相关产品推荐

