You don't have access to this class

Keep learning! Join and start boosting your career

Aprovecha el precio especial y haz tu profesi贸n a prueba de IA

Antes: $249

Currency
$209
Suscr铆bete

Termina en:

0 D铆as
3 Hrs
49 Min
28 Seg

Calorie Tracker: SearchScreen

12/20
Resources

In this class we will show you how to create a search screen using atomic components based on the single responsibility principle. In addition, we integrate the OpenFood API consumption functionality through a view model to ensure a clean and efficient architecture.

Why is the single responsibility principle crucial in NVM architecture?

The single responsibility principle is fundamental to the Model-View-ViewModel (MVVM) architecture. This principle dictates that a class, module or component should be responsible for a single task or responsibility within the system. Applying this approach has several advantages:

  • Maintainability: by having clear responsibilities, it is easier to update or modify one part of the system without affecting the others.
  • Decoupling: facilitates the isolation of each component, reducing the dependency between them.
  • Optimal unit testing: allows components to be tested in isolation and with greater efficiency.

How to create the searchTextField component?

Let's start by developing the main component of our search screen, the searchTextField. This component will handle the user's text input and act as the search field.

@Composable fun SearchTextField( text: String, onValueChange: (String) -> Unit, onSearch: () -> Unit, modifier: Modifier = Modifier, hint: String = stringResource(R.string.search), shouldShowHint: Boolean = true, onFocusChange: (FocusState) -> Unit ) { val localSpacing = LocalSpacing.current Box(modifier = modifier) { BasicTextField( value = text, onValueChange = onValueChange, singleLine = true, keyboardActions = KeyboardActions( onSearch = { onSearch() } ), keyboardOptions = KeyboardOptions( imeAction = ImeAction.Search ), modifier = Modifier .clip(RoundedCornerShape(5.dp)) .padding(2.dp) .shadow(2.dp, RoundedCornerShape(5.dp)) .background(MaterialTheme.colors.surface) .fillMaxWidth() .padding(localSpacing.spaceMedium) .onFocusChanged { onFocusChange(it) } .testTag("searchTextField") ) if (shouldShowHint) { Text( text = hint, style = MaterialTheme.typography.bodyLarge, fontWeight = FontWeight.Light, color = Color.LightGray, modifier = Modifier.align(Alignment.CenterStart).padding(localSpacing.spaceMedium) ) } } IconButton(onClick = onSearch, modifier = Modifier.align(Alignment.CenterEnd)) { Icon( imageVector = Icons.Default.Search, contentDescription = stringResource(R.string.search) ) } } } } } } }

How to implement a searchScreen?

Having a robust text component, we can move on to building the full search screen, leveraging the searchTextField composable we have developed.

@Composable fun SearchScreen( snackbarHostState: SnackbarHostState, mealName: String, dayOfTheMonth: Int, month: Int, year: Int, onNavigateUp: () -> Unit, viewModel: SearchViewModel = hiltViewModel() ) { val localSpacing = LocalSpacing.current val keyboardController = LocalSoftwareKeyboardController.current Column( modifier = Modifier.fillMaxSize().padding(localSpacing.spaceMedium) ) { Spacer(modifier = Modifier.height(localSpacing.spaceLarge)) Text( text = stringResource(id = R.string.add_meal_name, mealName), style = MaterialTheme.typography.titleMedium ) Spacer(modifier = Modifier.height(localSpacing.spaceMedium)) SearchTextField( text = "", onValueChange = {}, onSearch = { keyboardController?.hide() // Search Logic }, shouldShowHint = true, onFocusChange = {} ) Spacer(modifier = Modifier.height(localSpacing.spaceMedium)) } } }

How to integrate the viewModel to the search flow?

In an MVVM architecture, it is crucial to use a ViewModel to manage view states and business logic. Let's initialize our ViewModel to handle the search with the OpenFood API.

@HiltViewModel class SearchViewModel @Inject constructor( private val trackerUseCase: TrackerUseCase ) : ViewModel() { private val _uiEvent = Channel() val uiEvent = _uiEvent.receiveAsFlow() fun executeSearch() { viewModelScope.launch { // Implementing the search logic trackerUseCase.search("pizza") } } } } }

The code structure and modular design allow us to keep each component separate and focused on its specific task. This not only improves the development and maintenance experience, but also provides flexibility for future expansion. In addition, the use of a viewModel ensures that the business logic remains separate from the UI logic, which facilitates testing and maintenance.

Let's move forward, excited about our applications' ability to deliver dynamic and functional experiences, always optimizing each component we create.

Contributions 0

Questions 0

Sort by:

Want to see more contributions, questions and answers from the community?