Merge pull request #70 from google-developer-training/starter-e2e
Implement edge-to-edge on branch starter
This commit is contained in:
commit
4b6bf04748
|
@ -5,15 +5,12 @@ This app contains an order flow for cupcakes with options for quantity, flavor,
|
||||||
The order details get displayed on an order summary screen and can be shared to another app to
|
The order details get displayed on an order summary screen and can be shared to another app to
|
||||||
send the order.
|
send the order.
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
|
|
||||||
Pre-requisites
|
Pre-requisites
|
||||||
--------------
|
--------------
|
||||||
* Experience with Kotlin syntax.
|
* Experience with Kotlin syntax.
|
||||||
* How to create and run a project in Android Studio.
|
* How to create and run a project in Android Studio.
|
||||||
* How to create composable functions
|
* How to create composable functions
|
||||||
* TODO
|
|
||||||
|
|
||||||
|
|
||||||
Getting Started
|
Getting Started
|
||||||
|
|
|
@ -20,12 +20,12 @@ plugins {
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.example.cupcake"
|
namespace = "com.example.cupcake"
|
||||||
compileSdk = 33
|
compileSdk = 34
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.example.cupcake"
|
applicationId = "com.example.cupcake"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 33
|
targetSdk = 34
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ android {
|
||||||
compose = true
|
compose = true
|
||||||
}
|
}
|
||||||
composeOptions {
|
composeOptions {
|
||||||
kotlinCompilerExtensionVersion = "1.4.7"
|
kotlinCompilerExtensionVersion = "1.5.3"
|
||||||
}
|
}
|
||||||
packaging {
|
packaging {
|
||||||
resources {
|
resources {
|
||||||
|
@ -67,20 +67,20 @@ android {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation(platform("androidx.compose:compose-bom:2023.05.01"))
|
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
|
||||||
implementation("androidx.activity:activity-compose:1.7.2")
|
implementation("androidx.activity:activity-compose:1.8.0")
|
||||||
implementation("androidx.compose.material3:material3")
|
implementation("androidx.compose.material3:material3")
|
||||||
implementation("androidx.compose.runtime:runtime")
|
implementation("androidx.compose.runtime:runtime")
|
||||||
implementation("androidx.compose.runtime:runtime-livedata")
|
implementation("androidx.compose.runtime:runtime-livedata")
|
||||||
implementation("androidx.compose.ui:ui")
|
implementation("androidx.compose.ui:ui")
|
||||||
implementation("androidx.compose.ui:ui-graphics")
|
implementation("androidx.compose.ui:ui-graphics")
|
||||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||||
implementation("androidx.core:core-ktx:1.10.1")
|
implementation("androidx.core:core-ktx:1.12.0")
|
||||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:${rootProject.extra["lifecycle_version"]}")
|
implementation("androidx.lifecycle:lifecycle-livedata-ktx:${rootProject.extra["lifecycle_version"]}")
|
||||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:${rootProject.extra["lifecycle_version"]}")
|
implementation("androidx.lifecycle:lifecycle-runtime-ktx:${rootProject.extra["lifecycle_version"]}")
|
||||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:${rootProject.extra["lifecycle_version"]}")
|
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:${rootProject.extra["lifecycle_version"]}")
|
||||||
implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:${rootProject.extra["lifecycle_version"]}")
|
implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:${rootProject.extra["lifecycle_version"]}")
|
||||||
implementation("androidx.navigation:navigation-compose:2.5.3")
|
implementation("androidx.navigation:navigation-compose:2.7.4")
|
||||||
|
|
||||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||||
|
|
|
@ -18,13 +18,13 @@ package com.example.cupcake
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.activity.enableEdgeToEdge
|
||||||
import com.example.cupcake.ui.theme.CupcakeTheme
|
import com.example.cupcake.ui.theme.CupcakeTheme
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
enableEdgeToEdge()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
|
||||||
setContent {
|
setContent {
|
||||||
CupcakeTheme {
|
CupcakeTheme {
|
||||||
CupcakeApp()
|
CupcakeApp()
|
||||||
|
|
|
@ -39,6 +39,7 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import com.example.cupcake.R
|
import com.example.cupcake.R
|
||||||
import com.example.cupcake.ui.components.FormattedPriceLabel
|
import com.example.cupcake.ui.components.FormattedPriceLabel
|
||||||
|
import com.example.cupcake.ui.theme.CupcakeTheme
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Composable that displays the list of items as [RadioButton] options,
|
* Composable that displays the list of items as [RadioButton] options,
|
||||||
|
@ -52,14 +53,14 @@ fun SelectOptionScreen(
|
||||||
options: List<String>,
|
options: List<String>,
|
||||||
onSelectionChanged: (String) -> Unit = {},
|
onSelectionChanged: (String) -> Unit = {},
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
){
|
) {
|
||||||
var selectedValue by rememberSaveable { mutableStateOf("") }
|
var selectedValue by rememberSaveable { mutableStateOf("") }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
verticalArrangement = Arrangement.SpaceBetween
|
verticalArrangement = Arrangement.SpaceBetween
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium))){
|
Column(modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium))) {
|
||||||
options.forEach { item ->
|
options.forEach { item ->
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.selectable(
|
modifier = Modifier.selectable(
|
||||||
|
@ -70,7 +71,7 @@ fun SelectOptionScreen(
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
){
|
) {
|
||||||
RadioButton(
|
RadioButton(
|
||||||
selected = selectedValue == item,
|
selected = selectedValue == item,
|
||||||
onClick = {
|
onClick = {
|
||||||
|
@ -98,12 +99,14 @@ fun SelectOptionScreen(
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(dimensionResource(R.dimen.padding_medium))
|
.padding(dimensionResource(R.dimen.padding_medium)),
|
||||||
.weight(1f, false),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium)),
|
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium)),
|
||||||
verticalAlignment = Alignment.Bottom
|
verticalAlignment = Alignment.Bottom
|
||||||
){
|
) {
|
||||||
OutlinedButton(modifier = Modifier.weight(1f), onClick = {}) {
|
OutlinedButton(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
onClick = {}
|
||||||
|
) {
|
||||||
Text(stringResource(R.string.cancel))
|
Text(stringResource(R.string.cancel))
|
||||||
}
|
}
|
||||||
Button(
|
Button(
|
||||||
|
@ -116,15 +119,16 @@ fun SelectOptionScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun SelectOptionPreview(){
|
fun SelectOptionPreview() {
|
||||||
|
CupcakeTheme {
|
||||||
SelectOptionScreen(
|
SelectOptionScreen(
|
||||||
subtotal = "299.99",
|
subtotal = "299.99",
|
||||||
options = listOf("Option 1", "Option 2", "Option 3", "Option 4"),
|
options = listOf("Option 1", "Option 2", "Option 3", "Option 4"),
|
||||||
modifier = Modifier.fillMaxHeight()
|
modifier = Modifier.fillMaxHeight()
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ import androidx.annotation.StringRes
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
@ -40,6 +39,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.cupcake.R
|
import com.example.cupcake.R
|
||||||
import com.example.cupcake.data.DataSource
|
import com.example.cupcake.data.DataSource
|
||||||
|
import com.example.cupcake.ui.theme.CupcakeTheme
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Composable that allows the user to select the desired cupcake quantity and expects
|
* Composable that allows the user to select the desired cupcake quantity and expects
|
||||||
|
@ -50,7 +50,7 @@ import com.example.cupcake.data.DataSource
|
||||||
fun StartOrderScreen(
|
fun StartOrderScreen(
|
||||||
quantityOptions: List<Pair<Int, Int>>,
|
quantityOptions: List<Pair<Int, Int>>,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
){
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
verticalArrangement = Arrangement.SpaceBetween
|
verticalArrangement = Arrangement.SpaceBetween
|
||||||
|
@ -73,7 +73,6 @@ fun StartOrderScreen(
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(dimensionResource(R.dimen.padding_small)))
|
Spacer(modifier = Modifier.height(dimensionResource(R.dimen.padding_small)))
|
||||||
}
|
}
|
||||||
Row(modifier = Modifier.weight(1f, false)) {
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
@ -89,7 +88,6 @@ fun StartOrderScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,7 +99,7 @@ fun SelectQuantityButton(
|
||||||
@StringRes labelResourceId: Int,
|
@StringRes labelResourceId: Int,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
){
|
) {
|
||||||
Button(
|
Button(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = modifier.widthIn(min = 250.dp)
|
modifier = modifier.widthIn(min = 250.dp)
|
||||||
|
@ -112,9 +110,13 @@ fun SelectQuantityButton(
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun StartOrderPreview(){
|
fun StartOrderPreview() {
|
||||||
|
CupcakeTheme {
|
||||||
StartOrderScreen(
|
StartOrderScreen(
|
||||||
quantityOptions = DataSource.quantityOptions,
|
quantityOptions = DataSource.quantityOptions,
|
||||||
modifier = Modifier.fillMaxSize().padding(dimensionResource(R.dimen.padding_medium))
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(dimensionResource(R.dimen.padding_medium))
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||||
import com.example.cupcake.R
|
import com.example.cupcake.R
|
||||||
import com.example.cupcake.data.OrderUiState
|
import com.example.cupcake.data.OrderUiState
|
||||||
import com.example.cupcake.ui.components.FormattedPriceLabel
|
import com.example.cupcake.ui.components.FormattedPriceLabel
|
||||||
|
import com.example.cupcake.ui.theme.CupcakeTheme
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This composable expects [orderUiState] that represents the order state, [onCancelButtonClicked]
|
* This composable expects [orderUiState] that represents the order state, [onCancelButtonClicked]
|
||||||
|
@ -48,7 +49,7 @@ import com.example.cupcake.ui.components.FormattedPriceLabel
|
||||||
fun OrderSummaryScreen(
|
fun OrderSummaryScreen(
|
||||||
orderUiState: OrderUiState,
|
orderUiState: OrderUiState,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
){
|
) {
|
||||||
val resources = LocalContext.current.resources
|
val resources = LocalContext.current.resources
|
||||||
|
|
||||||
val numberOfCupcakes = resources.getQuantityString(
|
val numberOfCupcakes = resources.getQuantityString(
|
||||||
|
@ -95,9 +96,7 @@ fun OrderSummaryScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium))
|
||||||
.weight(1f, false)
|
|
||||||
.padding(dimensionResource(R.dimen.padding_medium))
|
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_small))
|
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_small))
|
||||||
|
@ -121,9 +120,11 @@ fun OrderSummaryScreen(
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun OrderSummaryPreview(){
|
fun OrderSummaryPreview() {
|
||||||
|
CupcakeTheme {
|
||||||
OrderSummaryScreen(
|
OrderSummaryScreen(
|
||||||
orderUiState = OrderUiState(0, "Test", "Test", "$300.00"),
|
orderUiState = OrderUiState(0, "Test", "Test", "$300.00"),
|
||||||
modifier = Modifier.fillMaxHeight()
|
modifier = Modifier.fillMaxHeight()
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,11 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
buildscript {
|
buildscript {
|
||||||
extra.apply {
|
extra.apply {
|
||||||
set("lifecycle_version", "2.6.1")
|
set("lifecycle_version", "2.6.2")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application") version "8.0.2" apply false
|
id("com.android.application") version "8.1.2" apply false
|
||||||
id("com.android.library") version "8.0.2" apply false
|
id("com.android.library") version "8.1.2" apply false
|
||||||
id("org.jetbrains.kotlin.android") version "1.8.21" apply false
|
id("org.jetbrains.kotlin.android") version "1.9.10" apply false
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#Sun Mar 19 17:30:22 PDT 2023
|
#Sun Mar 19 17:30:22 PDT 2023
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
Loading…
Reference in New Issue