Skip to content

Instantly share code, notes, and snippets.

@loloof64
Created December 22, 2022 12:44
Show Gist options
  • Save loloof64/f13d23007611c9f4c11343feee4009af to your computer and use it in GitHub Desktop.
Save loloof64/f13d23007611c9f4c11343feee4009af to your computer and use it in GitHub Desktop.
Simple Jetpack compose project : issue with App architecture being not responsive
package com.loloof64.compose_chess_experiment.ui.components.chess_board
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Button
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.sp
@Composable
fun ChessBoard(
position: String = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
) {
val positionParts = position.split(' ')
val isWhiteTurn = positionParts[1] == "w"
val bgColor = Color(0xFF9999FF)
BoxWithConstraints{
val heightBasedAspectRatio = maxHeight > maxWidth
val minAvailableSide = if (maxWidth < maxHeight) maxWidth else maxHeight
val cellSize = minAvailableSide * 0.11f
Column(modifier = Modifier
.aspectRatio(1f, heightBasedAspectRatio)
.background(bgColor)
) {
ChessBoardHorizontalLabels(cellSize = cellSize, whiteTurn = null)
(0..7).forEach {
val row = 8-it
val rowLabel = "${Char('0'.code + row)}"
val firstIsWhite = it % 2 == 0
ChessBoardCellsLine(cellSize = cellSize, firstCellWhite = firstIsWhite, rowLabel = rowLabel)
}
ChessBoardHorizontalLabels(cellSize = cellSize, whiteTurn = isWhiteTurn)
}
}
}
@Composable
private fun ChessBoardCellsLine(
modifier: Modifier = Modifier,
cellSize: Dp,
firstCellWhite: Boolean,
rowLabel: String,
) {
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically,
) {
ChessBoardVerticalLabel(text = rowLabel, cellSize = cellSize)
(0..7).forEach {
ChessBoardCell(isWhite = if ((it % 2) == 0) firstCellWhite else !firstCellWhite, size = cellSize)
}
ChessBoardVerticalLabel(text = rowLabel, cellSize = cellSize)
}
}
@Composable
private fun ChessBoardVerticalLabel(
modifier: Modifier = Modifier,
text: String,
cellSize: Dp,
) {
val fontSize = with(LocalDensity.current) {
(cellSize * 0.3f).toSp()
}
Column(modifier = modifier
.width(cellSize / 2)
.height(cellSize / 2)
.background(Color.Transparent),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(text, fontWeight = FontWeight.Bold, color = Color.Yellow, fontSize = fontSize)
}
}
@Composable
private fun ChessBoardHorizontalLabels(
modifier: Modifier = Modifier,
cellSize: Dp,
whiteTurn: Boolean?
) {
val fontSize = with(LocalDensity.current) {
(cellSize * 0.3f).toSp()
}
Row(modifier = modifier
.fillMaxWidth()
.height(cellSize / 2)
) {
Row(modifier = Modifier
.width(cellSize / 2)
.height(cellSize / 2)) {
Text(
text = "",
fontWeight = FontWeight.Bold,
color = Color.Transparent,
fontSize = fontSize
)
}
(0..7).forEach {
val col = it
val colLabel = "${Char('A'.code + col)}"
Row(modifier = Modifier
.width(cellSize)
.height(cellSize / 2),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically) {
Text(
text = colLabel,
fontWeight = FontWeight.Bold,
color = Color.Yellow,
fontSize = fontSize,
textAlign = TextAlign.Center,
)
}
}
if (whiteTurn == null) {
Row(modifier = Modifier
.width(cellSize / 2)
.height(cellSize / 2)) {
Text(
text = "",
fontWeight = FontWeight.Bold,
color = Color.Transparent,
fontSize = fontSize
)
}
}
else {
val color = if (whiteTurn) Color.White else Color.Black
Column(modifier = Modifier
.width(cellSize / 2)
.height(cellSize / 2)
.clip(CircleShape)
.background(color)
) {
}
}
}
}
@Composable
private fun ChessBoardCell(
modifier: Modifier = Modifier,
isWhite: Boolean,
size: Dp
) {
val bgColor = if (isWhite) Color(0xFFFFDEAD) else Color(0xFFCD853F)
Surface(modifier = modifier.size(size)) {
Column(modifier = Modifier.background(bgColor)) {
}
}
}
package com.loloof64.compose_chess_experiment.data.repositories
import com.github.bhlangonijr.chesslib.Board
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import javax.inject.Inject
data class ChessGame(
val currentPosition: String,
val playedPositions: Map<String, Int>
) {
companion object {
const val emptyPosition = "8/8/8/8/8/8/8/8 w - - 0 1"
const val defaultPosition = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
}
private var board = Board()
init {
board.loadFromFen(currentPosition)
}
}
class ChessGameRepository @Inject constructor() {
private val _game = MutableStateFlow(
ChessGame(
currentPosition = ChessGame.emptyPosition,
playedPositions = mapOf()
)
)
val game: Flow<ChessGame> = _game
fun newGame(startPosition: String = ChessGame.defaultPosition) {
_game.update { currentState ->
currentState.copy(
currentPosition = startPosition,
playedPositions = mapOf(startPosition to 1)
)
}
}
}
package com.loloof64.compose_chess_experiment.ui.screens.game
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import com.loloof64.compose_chess_experiment.R
import com.loloof64.compose_chess_experiment.ui.components.chess_board.ChessBoard
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun GameScreen(
viewModel: ChessGameViewModel = viewModel()
) {
val state = viewModel.uiState.collectAsState()
Scaffold(
topBar = {
TopAppBar(
title = { Text(stringResource(id = R.string.game_page)) }
)
}
) {
Column(
modifier = Modifier.padding(it),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Button(onClick = {
viewModel.newGame()
}) {
Text(text = "New game")
}
ChessBoard(
position = state.value.currentPosition
)
}
}
}
package com.loloof64.compose_chess_experiment.ui.screens.game
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.loloof64.compose_chess_experiment.data.repositories.ChessGame
import com.loloof64.compose_chess_experiment.data.repositories.ChessGameRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject
data class ChessGameUiState(
val currentPosition: String,
)
@HiltViewModel
class ChessGameViewModel @Inject constructor(
private val gameRepository: ChessGameRepository
) : ViewModel() {
val uiState : StateFlow<ChessGameUiState> = gameRepository.game.map {
return@map ChessGameUiState(currentPosition = it.currentPosition)
}.stateIn(
initialValue = ChessGameUiState(currentPosition = ChessGame.emptyPosition),
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000)
)
fun newGame(startPosition: String = ChessGame.defaultPosition) {
viewModelScope.launch(Dispatchers.Default) {
gameRepository.newGame(startPosition)
}
}
}
package com.loloof64.compose_chess_experiment
import android.app.Application
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.loloof64.compose_chess_experiment.ui.screens.game.GameScreen
import com.loloof64.compose_chess_experiment.ui.theme.ComposeChessExperimentTheme
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class MyApplication: Application()
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
App()
}
}
}
@Composable
fun App() {
ComposeChessExperimentTheme {
// A surface container using the 'background' color from the theme
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
GameScreen()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment