Modificar cenas com decoradores de cena

Os decoradores de cena permitem modificar a cena calculada pela estratégia de cena do app. Na verdade, eles são usados para uma segunda fase de construção do conteúdo exibido por um NavDisplay.

Essa abordagem permite encapsular funcionalidades específicas, como a exibição de componentes comuns da interface, em decoradores de cena individuais.

Por exemplo, considere um app de produtividade que tenha três rotas de nível superior: uma caixa de entrada de e-mail, uma caixa de entrada de mensagens diretas e uma visualização de calendário. Esse app pode usar dois decoradores de cena: um para adicionar uma barra de apps superior que exibe informações e controles para a rota de nível superior atual e outro para adicionar uma barra de navegação ou trilho persistente para navegar entre as rotas.

Criar uma estratégia de decorador de cena

Os decoradores de cena seguem um padrão semelhante às estratégias de cena. Para definir um decorador de cena, implemente a interface SceneDecoratorStrategy. Essa interface tem um método, decorateScene, que é análogo ao calculateScene método da SceneStrategy interface. decorateScene determina se pode decorar a cena:

  • Se a estratégia de decorador de cena não decorar a cena de entrada, ela vai retornar a cena de entrada como está.
  • Se ela decorar a cena de entrada, vai retornar uma nova Scene. Em geral, a cena retornada usa a cena de entrada como um parâmetro e chama o método content da cena de entrada no próprio método content.

Para determinar se e como a cena de entrada precisa ser decorada, a estratégia de decorador de cena pode considerar os metadados da Scene de entrada e das entradas contidas nessa cena.

class MySceneDecoratorStrategy<T : Any> : SceneDecoratorStrategy<T> {


    override fun SceneDecoratorStrategyScope<T>.decorateScene(scene: Scene<T>): Scene<T> {
        // `shouldDecorate` determines if the scene should be decorated based on scene.metadata,
        // scene.entries.metadata, or any other relevant state.
        return if (shouldDecorate(scene)) {
            MyDecoratingScene(scene)
        } else {
            scene
        }
    }

}

data class MyDecoratingScene<T : Any>(
    val scene: Scene<T>
) : Scene<T> {

    // ...

    override val content = @Composable {
        scene.content()
    }
}

Usar estratégias de decorador de cena

Para usar estratégias de decorador de cena, forneça-as ao NavDisplay usando o parâmetro sceneDecoratorStrategies. Ao decorar cenas, NavDisplay chama o método decorateScene de cada estratégia em sucessão, transmitindo a saída de cada chamada como entrada para a próxima.

NavDisplay(
    // ...
    sceneDecoratorStrategies = listOf(firstSceneDecoratorStrategy, secondSceneDecoratorStrategy)
)

Padrões comuns para decoradores de cena

Ao implementar decoradores de cena, estes são alguns padrões comuns que você precisa conhecer:

Copiar propriedades

Em muitos casos, a cena retornada pela decoração de uma cena precisa conter as mesmas entradas e ter as mesmas entradas anteriores da cena que está decorando. Além disso, ela provavelmente precisa herdar (ou modificar) os metadados da cena que está decorando, em vez de usar o comportamento padrão. O código a seguir demonstra um exemplo de como fazer isso:

data class CopyingScene<T : Any>(
    val scene: Scene<T>
) : Scene<T> {
    override val entries = scene.entries
    override val previousEntries = scene.previousEntries
    override val metadata = scene.metadata

    // ...
}

Manter animações

Conforme detalhado em Animar entre destinos, NavDisplay automaticamente anima as transições entre cenas quando uma chave derivada da classe da cena atual e da propriedade key muda.

Ao introduzir decoradores de cena no app, a classe da cena retornada após a decoração da cena pode permanecer a mesma, mesmo quando a classe da cena retornada durante o cálculo da cena muda. Quando isso acontece e as cenas de decoração copiam diretamente a key da cena que estão decorando, as animações integradas não acontecem mais porque a chave derivada não muda.

Para manter o suporte à animação integrada, as cenas de decoração precisam usar uma chave derivada da classe e da key da cena retornada por calculateScene.

data class DerivedKeyScene<T : Any>(
    val scene: Scene<T>
) : Scene<T> {
    override val key = scene::class to scene.key

    // ...
}