Added Bookshelf page for easier sorting
Renamed App Few cosmetic tweaks for mobile so it looks nicer Changed App Icon, Using Ophelia for placeholder. Maybe I'll just put her in a hat later.
This commit is contained in:
parent
d8bbce38d7
commit
309304b51a
18 changed files with 151 additions and 76 deletions
42
README.md
42
README.md
|
@ -1,42 +0,0 @@
|
|||
## Recipe App Compose Multiplatform
|
||||
|
||||
Inspired by [Roaa Kadam](https://github.com/Roaa94) flutter [app](https://github.com/Roaa94/recipes_ui_app/), I wanted to do the same in Compose Multiplatform. There is a lot to explore in Compose Multiplatform from the aspect of this app like Heor Animation, Collapsable Toolbar, Staggered Animations, Gyroscope detection etc.
|
||||
|
||||
> **Note**
|
||||
> It is still a work in progress
|
||||
|
||||
|
||||
### Live
|
||||
You can find it live [here](https://seabdulbasit.github.io/recipe-app/)
|
||||
|
||||
## Supported Platforms
|
||||
- Android
|
||||
- iOS
|
||||
- Desktop
|
||||
- Web
|
||||
- Android TV
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
## Demo
|
||||
|
||||
### iOS Demo
|
||||
https://youtu.be/MZDgPtjTiIs
|
||||
|
||||
### Web Demo
|
||||
https://www.youtube.com/watch?v=MZDgPtjTiIs&ab_channel=AbdulBasit
|
||||
|
||||
### Desktop Demo
|
||||
https://www.youtube.com/watch?v=6mWrp_-MxW8&ab_channel=AbdulBasit
|
||||
|
||||
|
||||
|
||||
<img width="615" alt="Screenshot 2023-06-22 at 11 49 28 AM" src="https://github.com/SEAbdulbasit/recipe-app/assets/33172684/ac19c301-8263-4d2c-8cfc-58f27d1acdb3">
|
||||
|
||||
|
||||
## Video Demo
|
||||
You can watch the video demo [here](https://www.youtube.com/watch?v=99i21nB4sI0&ab_channel=AbdulBasit)
|
||||
|
|
@ -15,7 +15,7 @@ class MainActivity : AppCompatActivity() {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, true)
|
||||
|
||||
setContent {
|
||||
val view = LocalView.current
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<background android:drawable="@drawable/ophelia_background" />
|
||||
<foreground android:drawable="@drawable/ophelia" />
|
||||
</adaptive-icon>
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<background android:drawable="@drawable/ophelia_background" />
|
||||
<foreground android:drawable="@drawable/ophelia" />
|
||||
</adaptive-icon>
|
|
@ -1,3 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">Recipe App</string>
|
||||
<string name="app_name">Baker\'s Menagerie</string>
|
||||
</resources>
|
10
androidApp/src/main/res/drawable-v24/ophelia.xml
Normal file
10
androidApp/src/main/res/drawable-v24/ophelia.xml
Normal file
File diff suppressed because one or more lines are too long
10
androidApp/src/main/res/drawable-v24/ophelia_background.xml
Normal file
10
androidApp/src/main/res/drawable-v24/ophelia_background.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#7dae7d"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
</vector>
|
|
@ -1,3 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">Recipe App</string>
|
||||
<string name="app_name">Baker\'s Menagerie</string>
|
||||
</resources>
|
|
@ -2,7 +2,7 @@ import androidx.compose.ui.window.Window
|
|||
import androidx.compose.ui.window.application
|
||||
|
||||
fun main() = application {
|
||||
Window(title = "Recipe App", onCloseRequest = ::exitApplication) {
|
||||
Window(title = "Baker's Menagerie", onCloseRequest = ::exitApplication) {
|
||||
MainView()
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="manifest" href="manifest.json"/>
|
||||
<title>Recipe App KMP</title>
|
||||
<title>Baker's Menagerie KMP</title>
|
||||
<script type="application/javascript" src="skiko.js"></script>
|
||||
<script type="application/javascript" src="webApp.js"></script>
|
||||
</head>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "Recipe App",
|
||||
"name": "Baker's Menagerie",
|
||||
"icons": [
|
||||
{
|
||||
"src": "images/logo.png",
|
||||
|
|
|
@ -60,6 +60,11 @@
|
|||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@js-joda/core@3.2.0":
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273"
|
||||
integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg==
|
||||
|
||||
"@leichtgewicht/ip-codec@^2.0.1":
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
|
||||
|
|
|
@ -1,10 +1,21 @@
|
|||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||
import androidx.compose.animation.SharedTransitionLayout
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Text
|
||||
|
@ -14,11 +25,15 @@ import androidx.compose.runtime.mutableStateListOf
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import details.RecipeDetails
|
||||
import recipeslist.RecipesListScreen
|
||||
import sensor.SensorManager
|
||||
|
@ -67,6 +82,7 @@ fun App(sensorManager: SensorManager?, isLarge: Boolean = false) {
|
|||
var search by remember { mutableStateOf("") }
|
||||
val tags = remember { mutableStateListOf<String>() }
|
||||
val recipeTags by remember { mutableStateOf(mutableListOf<String>()) }
|
||||
var book by remember { mutableStateOf("") }
|
||||
|
||||
for (recipe in getRecipeList()) {
|
||||
for (tag in recipe.tags) {
|
||||
|
@ -83,7 +99,7 @@ fun App(sensorManager: SensorManager?, isLarge: Boolean = false) {
|
|||
}
|
||||
}
|
||||
|
||||
val filteredItems = getFilteredRecipeList(tags, search)
|
||||
val filteredItems = getFilteredRecipeList(tags, search, book)
|
||||
var currentRecipe = getRecipeList().first()
|
||||
|
||||
if (filteredItems.isNotEmpty())
|
||||
|
@ -93,13 +109,6 @@ fun App(sensorManager: SensorManager?, isLarge: Boolean = false) {
|
|||
Column {
|
||||
if (searchBar) {
|
||||
Row {
|
||||
// Button(
|
||||
// onClick = {
|
||||
// onThemeToggle.invoke()
|
||||
// }
|
||||
// ) {
|
||||
// Text(text = theme.name)
|
||||
// }
|
||||
Button(
|
||||
onClick = {
|
||||
show = true
|
||||
|
@ -119,7 +128,6 @@ fun App(sensorManager: SensorManager?, isLarge: Boolean = false) {
|
|||
}
|
||||
|
||||
SharedTransitionLayout {
|
||||
val sharedTransitionScope = this
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = "Home",
|
||||
|
@ -127,13 +135,30 @@ fun App(sensorManager: SensorManager?, isLarge: Boolean = false) {
|
|||
) {
|
||||
composable(route = "Home")
|
||||
{
|
||||
HomeScreen {
|
||||
searchBar = true
|
||||
navController.navigate(RecipeAppScreen.List.name)
|
||||
|
||||
}
|
||||
HomeScreen(
|
||||
isLarge = isLarge,
|
||||
onClick = {
|
||||
searchBar = false
|
||||
navController.navigate("BookShelf")
|
||||
}
|
||||
)
|
||||
}
|
||||
composable(route = RecipeAppScreen.List.name) {
|
||||
composable(route = "BookShelf")
|
||||
{
|
||||
BookShelf(
|
||||
isLarge = isLarge,
|
||||
tags = recipeTags,
|
||||
onClick = { lockedTag ->
|
||||
book = lockedTag
|
||||
searchBar = true
|
||||
navController.navigate(RecipeAppScreen.List.name)
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = RecipeAppScreen.List.name
|
||||
) {
|
||||
RecipesListScreen(
|
||||
isLarge = isLarge,
|
||||
items = filteredItems,
|
||||
|
@ -158,4 +183,54 @@ fun App(sensorManager: SensorManager?, isLarge: Boolean = false) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BookShelf(
|
||||
onClick: (String) -> Unit,
|
||||
isLarge: Boolean,
|
||||
tags: List<String>
|
||||
) {
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 10.dp)
|
||||
.align(Alignment.Center),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
|
||||
Text(text = "What Do You Want To Cook Today?", textAlign = TextAlign.Center)
|
||||
|
||||
Button(onClick = { onClick("") }) {
|
||||
Text("ANYTHING!")
|
||||
}
|
||||
|
||||
val listState = rememberLazyGridState()
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(if(isLarge) 4 else 2),
|
||||
state = listState
|
||||
)
|
||||
{
|
||||
if(isLarge.not()) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars))
|
||||
}
|
||||
items(tags.size) {item ->
|
||||
val tag = tags[item]
|
||||
Button(onClick = {onClick(tag) }) {
|
||||
Text(tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,15 +16,21 @@ fun List<String>.containsPartial(text:String) : Boolean {
|
|||
fun getFilteredRecipeList(
|
||||
tags : List<String>,
|
||||
search : String,
|
||||
lockTag : String,
|
||||
) : List<Recipe> {
|
||||
val items = getRecipeList()
|
||||
|
||||
var recipes = items.filter { it.tags.containsAll(tags)}
|
||||
var recipes = items.filter { it.tags.containsAll(tags) }
|
||||
|
||||
recipes = recipes.filter {
|
||||
it.title.contains(search) || it.ingredients.containsPartial(search)
|
||||
}
|
||||
|
||||
if (lockTag != "")
|
||||
{
|
||||
recipes = recipes.filter { it.tags.contains(lockTag)}
|
||||
}
|
||||
|
||||
return recipes
|
||||
}
|
||||
|
||||
|
|
|
@ -11,21 +11,29 @@ import androidx.compose.material.MaterialTheme
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun HomeScreen(
|
||||
onClick: () -> Unit,
|
||||
isLarge: Boolean,
|
||||
) {
|
||||
val text = "Recipes"
|
||||
HeaderText(text, onClick)
|
||||
HeaderText(text, onClick, isLarge)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HeaderText(
|
||||
text: String,
|
||||
onClick: () -> Unit
|
||||
onClick: () -> Unit,
|
||||
isLarge: Boolean
|
||||
) {
|
||||
|
||||
val header = if(isLarge) MaterialTheme.typography.h1 else MaterialTheme.typography.h2
|
||||
val lowHead = if(isLarge) MaterialTheme.typography.h3 else MaterialTheme.typography.h4
|
||||
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
|
@ -41,10 +49,13 @@ fun HeaderText(
|
|||
Spacer(modifier = Modifier.weight(1f))
|
||||
androidx.compose.material3.Text (
|
||||
text = "Baker's Menagerie",
|
||||
style = MaterialTheme.typography.h1
|
||||
style = header,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
androidx.compose.material3.Text (
|
||||
text = "An Allbright Family Cookbook"
|
||||
text = "An Allbright Family Cookbook",
|
||||
style = lowHead,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
androidx.compose.material3.Button(
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">Recipe App</string>
|
||||
<string name="app_name">Baker\'s Menagerie</string>
|
||||
</resources>
|
|
@ -5,7 +5,7 @@
|
|||
<link rel="manifest" href="manifest.json"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link type="text/css" rel="stylesheet" href="styles.css">
|
||||
<title>Recipe App KMP</title>
|
||||
<title>Baker's Menagerie KMP</title>
|
||||
<script type="application/javascript" src="webApp.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "Recipe App",
|
||||
"name": "Baker's Menagerie",
|
||||
"icons": [
|
||||
{
|
||||
"src": "images/logo.png",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue