0

I am building a simple app following Mitch Tabian's youtube tutorial about Jetpack Compose. In the State Hoisting video, he extracts the code for the search TextField into a separate Composable. When I do so, my textField doesn't update the value and I can't find what I am doing wrong.

SearchAppBar Composable

@Composable
fun SearchAppBar(
  query: String,
  onQueryChanged: (String) -> Unit,
  onExecuteSearch: () -> Unit,
  selectedCategory: FoodCategory?,
  onSelectedCategoryChanged: (String) -> Unit
) {
  Surface(
    modifier = Modifier
      .fillMaxWidth(),
    color = Color.White,
    elevation = 4.dp
  ) {
    Column {
      Row(modifier = Modifier.fillMaxWidth()) {
        val focusManager = LocalFocusManager.current
        OutlinedTextField(
          value = query,
          onValueChange = { newValue -> onQueryChanged(newValue) },
          modifier = Modifier
            .background(color = MaterialTheme.colors.surface)
            .fillMaxWidth()
            .padding(8.dp),
          label = {
            Text(text = "Search")
          },
...

Fragment

class RecipeListFragment : Fragment() {
  private val viewModel: RecipeListViewModel by viewModels()

  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    return ComposeView(requireContext()).apply {
      setContent {
        val recipes = viewModel.recipes.value
        val query = viewModel.query.value
        val selectedCategory = viewModel.selectedCategory.value
        Column {
          SearchAppBar(
            query = query,
            onQueryChanged = { viewModel.onQueryChanged(query) },
            onExecuteSearch = { viewModel::newSearch },
            selectedCategory = selectedCategory,
            onSelectedCategoryChanged = { viewModel::onSelectedCategoryChanged })
          LazyColumn {
            itemsIndexed(items = recipes) { index, recipe ->
              RecipeCard(recipe = recipe, onClick = { })
            }
          }
        }
      }
    }
  }
}

ViewModel

class RecipeListViewModel @Inject constructor(private val repository: RecipeRepository, @Named("auth_token") private val token: String) : ViewModel() {
  val recipes: MutableState<List<Recipe>> = mutableStateOf(listOf())
  val query = mutableStateOf("")
  val selectedCategory: MutableState<FoodCategory?> = mutableStateOf(null)

  init {
    newSearch()
  }

  fun onQueryChanged(query: String) {
    this.query.value = query
  }

  fun newSearch() {
    viewModelScope.launch {
      recipes.value = repository.search(token = token, page = 1, query = query.value)
    }
  }

  fun onSelectedCategoryChanged(category: String) {
    val newCategory = getFoodCategory(category)
    selectedCategory.value = newCategory
    onQueryChanged(category)
  }
}
Praveen P.
  • 331
  • 2
  • 10

1 Answers1

0

The following are no longer states that are observed.

val recipes = viewModel.recipes.value
val query = viewModel.query.value
val selectedCategory = viewModel.selectedCategory.value

Delay the .value call or use the var by viewModel.recipes or use restructuring val (recipes, _) = viewModel.recipes

2jan222
  • 576
  • 5
  • 18
  • changed the variables as suggested, but it's still not working `val recipes by viewModel.recipes val query: String by viewModel.query val selectedCategory by viewModel.selectedCategory` I also tried replacing the query `MutableState` in the viewModel by `MutableLiveData` and then `observeAsState` in the fragment. And also not working – Praveen P. Feb 28 '21 at 12:25
  • 1
    I dont see another error atm, have a look at FlowState. https://developer.android.com/kotlin/flow/stateflow-and-sharedflow Its more intuitive with Compose. It has a .collectAsState Extention function you can use. – 2jan222 Feb 28 '21 at 13:42
  • found the issue finally... I just had to remove the curly braces from this line in my fragment `onQueryChanged = { viewModel.onQueryChanged(query) }`like so `onQueryChanged = viewModel.onQueryChanged(query)` – Praveen P. Feb 28 '21 at 16:34