Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

233
Views
Se observa un cambio en tiempo real de Firestore con StateFlow en el repositorio y el modelo de vista, pero el adaptador no se actualiza

Como desarrollador uno necesita adaptarse al cambio, leí en alguna parte que dice:

Si no elige la arquitectura adecuada para su proyecto de Android, tendrá dificultades para mantenerla a medida que crece su base de código y se expande su equipo.

Quería implementar Clean Architecture con MVVM

El flujo de datos de mi aplicación se verá así:

OneNote_Clean_Architecture_MVVM_Data_Flow

clase de modelo

 data class Note( val title: String? = null, val timestamp: String? = null )

Dtos

 data class NoteRequest( val title: String? = null, val timestamp: String? = null )

y

 data class NoteResponse( val id: String? = null, val title: String? = null, val timestamp: String? = null )

La capa de repositorio es

 interface INoteRepository { fun getNoteListSuccessListener(success: (List<NoteResponse>) -> Unit) fun deleteNoteSuccessListener(success: (List<NoteResponse>) -> Unit) fun getNoteList() fun deleteNoteById(noteId: String) }

NoteRepositoryImpl es:

 class NoteRepositoryImpl: INoteRepository { private val mFirebaseFirestore = Firebase.firestore private val mNotesCollectionReference = mFirebaseFirestore.collection(COLLECTION_NOTES) private val noteList = mutableListOf<NoteResponse>() private var getNoteListSuccessListener: ((List<NoteResponse>) -> Unit)? = null private var deleteNoteSuccessListener: ((List<NoteResponse>) -> Unit)? = null override fun getNoteListSuccessListener(success: (List<NoteResponse>) -> Unit) { getNoteListSuccessListener = success } override fun deleteNoteSuccessListener(success: (List<NoteResponse>) -> Unit) { deleteNoteSuccessListener = success } override fun getNoteList() { mNotesCollectionReference .addSnapshotListener { value, _ -> noteList.clear() if (value != null) { for (item in value) { noteList .add(item.toNoteResponse()) } getNoteListSuccessListener?.invoke(noteList) } Log.e("NOTE_REPO", "$noteList") } } override fun deleteNoteById(noteId: String) { mNotesCollectionReference.document(noteId) .delete() .addOnSuccessListener { deleteNoteSuccessListener?.invoke(noteList) } } }

La capa ViewModel es:

 interface INoteViewModel { val noteListStateFlow: StateFlow<List<NoteResponse>> val noteDeletedStateFlow: StateFlow<List<NoteResponse>> fun getNoteList() fun deleteNoteById(noteId: String) }

NoteViewModelImpl es:

 class NoteViewModelImpl: ViewModel(), INoteViewModel { private val mNoteRepository: INoteRepository = NoteRepositoryImpl() private val _noteListStateFlow = MutableStateFlow<List<NoteResponse>>(mutableListOf()) override val noteListStateFlow: StateFlow<List<NoteResponse>> get() = _noteListStateFlow.asStateFlow() private val _noteDeletedStateFlow = MutableStateFlow<List<NoteResponse>>(mutableListOf()) override val noteDeletedStateFlow: StateFlow<List<NoteResponse>> get() = _noteDeletedStateFlow.asStateFlow() init { // getNoteListSuccessListener mNoteRepository .getNoteListSuccessListener { viewModelScope .launch { _noteListStateFlow.emit(it) Log.e("NOTE_G_VM", "$it") } } // deleteNoteSuccessListener mNoteRepository .deleteNoteSuccessListener { viewModelScope .launch { _noteDeletedStateFlow.emit(it) Log.e("NOTE_D_VM", "$it") } } } override fun getNoteList() { // Get all notes mNoteRepository.getNoteList() } override fun deleteNoteById(noteId: String) { mNoteRepository.deleteNoteById(noteId = noteId) } }

y por último, pero no menos importante, Fragment es:

 class HomeFragment : Fragment() { private lateinit var binding: FragmentHomeBinding private val viewModel: INoteViewModel by viewModels<NoteViewModelImpl>() private lateinit var adapter: NoteAdapter override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { binding = FragmentHomeBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val recyclerView = binding.recyclerViewNotes recyclerView.addOnScrollListener( ExFABScrollListener(binding.fab) ) adapter = NoteAdapter{itemView, noteId -> if (noteId != null) { showMenu(itemView, noteId) } } recyclerView.adapter = adapter // initView() fetchFirestoreData() binding.fab.setOnClickListener { val action = HomeFragmentDirections.actionFirstFragmentToSecondFragment() findNavController().navigate(action) } } private fun fetchFirestoreData() { // Get note list viewModel .getNoteList() // Create list object val noteList:MutableList<NoteResponse> = mutableListOf() // Impose StateFlow viewModel .noteListStateFlow .onEach { data -> data.forEach {noteResponse -> noteList.add(noteResponse) adapter.submitList(noteList) Log.e("NOTE_H_FRAG", "$noteResponse") } }.launchIn(viewLifecycleOwner.lifecycleScope) } //In the showMenu function from the previous example: @SuppressLint("RestrictedApi") private fun showMenu(v: View, noteId: String) { val menuBuilder = MenuBuilder(requireContext()) SupportMenuInflater(requireContext()).inflate(R.menu.menu_note_options, menuBuilder) menuBuilder.setCallback(object : MenuBuilder.Callback { override fun onMenuItemSelected(menu: MenuBuilder, item: MenuItem): Boolean { return when(item.itemId){ R.id.option_edit -> { val action = HomeFragmentDirections.actionFirstFragmentToSecondFragment(noteId = noteId) findNavController().navigate(action) true } R.id.option_delete -> { viewModel .deleteNoteById(noteId = noteId) // Create list object val noteList:MutableList<NoteResponse> = mutableListOf() viewModel .noteDeletedStateFlow .onEach {data -> data.forEach {noteResponse -> noteList.add(noteResponse) adapter.submitList(noteList) Log.e("NOTE_H_FRAG", "$noteResponse") } }.launchIn(viewLifecycleOwner.lifecycleScope) true } else -> false } } override fun onMenuModeChange(menu: MenuBuilder) {} }) val menuHelper = MenuPopupHelper(requireContext(), menuBuilder, v) menuHelper.setForceShowIcon(true) // show icons!!!!!!!! menuHelper.show() } }

Con toda la lógica anterior, me enfrento a TWO problemas

problema - 1 Como se menciona aquí , he agregado SnapshotListener en la colección como:

 override fun getNoteList() { mNotesCollectionReference .addSnapshotListener { value, _ -> noteList.clear() if (value != null) { for (item in value) { noteList .add(item.toNoteResponse()) } getNoteListSuccessListener?.invoke(noteList) } Log.e("NOTE_REPO", "$noteList") } }

con él, si cambio los valores de un documento desde Firebase Console , obtengo valores actualizados en Repository y ViewModel , pero la lista de notas no se actualiza y se pasa al adapter , por lo que todos los elementos son iguales.

problema - 2
Si elimino cualquier elemento de la vista de lista/reciclador usando:

 R.id.option_delete -> { viewModel .deleteNoteById(noteId = noteId) // Create list object val noteList:MutableList<NoteResponse> = mutableListOf() viewModel .noteDeletedStateFlow .onEach {data -> data.forEach {noteResponse -> noteList.add(noteResponse) adapter.submitList(noteList) Log.e("NOTE_H_FRAG", "$noteResponse") } }.launchIn(viewLifecycleOwner.lifecycleScope)

Todavía obtengo una lista actualizada (es decir, una nueva lista de notas que excluye la nota eliminada) en el Repository y ViewModel , pero la lista de notas no se actualiza y se pasa al adapter , por lo que todos los elementos son iguales, no y exclusión del elemento eliminado.

Pregunta ¿Dónde exactamente estoy cometiendo un error al inicializar/actualizar el adaptador? porque ViewModel y Repository funcionan bien.

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

Intente eliminar listas adicionales de elementos en los fetchFirestoreData() y showMenu() (para el elemento R.id.option_delete ) del fragmento HomeFragment y vea si funciona:

 // remove `val noteList:MutableList<NoteResponse>` in `fetchFirestoreData()` method private fun fetchFirestoreData() { ... // remove this line val noteList:MutableList<NoteResponse> = mutableListOf() // Impose StateFlow viewModel .noteListStateFlow .onEach { data -> adapter.submitList(data) }.launchIn(viewLifecycleOwner.lifecycleScope) }

Y lo mismo para el elemento del menú Eliminar ( R.id.option_delete ).

over 4 years ago · Santiago Trujillo Report

0

Realice los siguientes cambios: en el bloque init{} de NoteViewModelImpl :

 // getNoteListSuccessListener mNoteRepository .getNoteListSuccessListener{noteResponseList -> viewModelScope.launch{ _noteListStateFlow.emit(it.toList()) } }

debe agregar .toList() si desea emit una lista en StateFlow para recibir notificaciones sobre actualizaciones y en HomeFragment

 private fun fetchFirestoreData() { // Get note list viewModel .getNoteList() // Impose StateFlow lifecycleScope.launch { viewModel.noteListStateFlow.collect { list -> adapter.submitList(list.toMutableList()) } } }

Eso es todo, espero que funcione bien.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!