[go: nahoru, domu]

Add positional thresholds to BottomDrawerLayout

This CL introduces a positional thresholds parameter in stateDraggable.
Thresholds are specified as a lambda that takes a pair of anchors and
returns a value between them. The order of the anchors matters since it
indicates the drag direction (i.e. from the first anchor to the second).
Two default threshold constructors are provided: fixed and fractional.
BottomDrawerLayout uses a fixed threshold of 56dp or an app bar height.

Bug: 158805662
Bug: 158806527
Test: Ran DrawerTest and SwitchTest
Relnote: "Added thresholds param in stateDraggable to specify thresholds
between anchors. This was used to set a 56dp threshold in bottom drawer.
Also BottomDrawerLayout now uses a separate BottomDrawerState enum."
Change-Id: I533fad3d3bf9b95f702156e321aa15a84e81819b
diff --git a/ui/ui-material/api/0.1.0-dev15.txt b/ui/ui-material/api/0.1.0-dev15.txt
index 289b9e3..6b26c47 100644
--- a/ui/ui-material/api/0.1.0-dev15.txt
+++ b/ui/ui-material/api/0.1.0-dev15.txt
@@ -19,6 +19,14 @@
     method @androidx.compose.Composable public static void TopAppBar-oP-1cd0(androidx.ui.core.Modifier modifier = Modifier, long backgroundColor = MaterialTheme.colors.primarySurface, long contentColor = contentColorFor(backgroundColor), float elevation = androidx.ui.material.AppBarKt.TopAppBarElevation, kotlin.jvm.functions.Function1<? super androidx.ui.layout.RowScope,kotlin.Unit> content);
   }
 
+  public enum BottomDrawerState {
+    method public static androidx.ui.material.BottomDrawerState valueOf(String name) throws java.lang.IllegalArgumentException;
+    method public static androidx.ui.material.BottomDrawerState[] values();
+    enum_constant public static final androidx.ui.material.BottomDrawerState Closed;
+    enum_constant public static final androidx.ui.material.BottomDrawerState Expanded;
+    enum_constant public static final androidx.ui.material.BottomDrawerState Opened;
+  }
+
   public final class BottomNavigationKt {
     method @androidx.compose.Composable public static void BottomNavigation-oP-1cd0(androidx.ui.core.Modifier modifier = Modifier, long backgroundColor = MaterialTheme.colors.primarySurface, long contentColor = contentColorFor(backgroundColor), float elevation = androidx.ui.material.BottomNavigationKt.BottomNavigationElevation, kotlin.jvm.functions.Function1<? super androidx.ui.layout.RowScope,kotlin.Unit> content);
     method @androidx.compose.Composable public static void BottomNavigationItem-dOPBtLY(kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> text = emptyContent(), boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, androidx.ui.core.Modifier modifier = Modifier, boolean alwaysShowLabels = true, long activeColor = contentColor(), long inactiveColor = EmphasisAmbient.current.medium.applyEmphasis(activeColor));
@@ -104,7 +112,7 @@
   }
 
   public final class DrawerKt {
-    method @androidx.compose.Composable public static void BottomDrawerLayout-s-rmCOo(androidx.ui.material.DrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.DrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
+    method @androidx.compose.Composable public static void BottomDrawerLayout-8vp1Knk(androidx.ui.material.BottomDrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.BottomDrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
     method @androidx.compose.Composable public static void ModalDrawerLayout-s-rmCOo(androidx.ui.material.DrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.DrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
   }
 
diff --git a/ui/ui-material/api/current.txt b/ui/ui-material/api/current.txt
index 289b9e3..6b26c47 100644
--- a/ui/ui-material/api/current.txt
+++ b/ui/ui-material/api/current.txt
@@ -19,6 +19,14 @@
     method @androidx.compose.Composable public static void TopAppBar-oP-1cd0(androidx.ui.core.Modifier modifier = Modifier, long backgroundColor = MaterialTheme.colors.primarySurface, long contentColor = contentColorFor(backgroundColor), float elevation = androidx.ui.material.AppBarKt.TopAppBarElevation, kotlin.jvm.functions.Function1<? super androidx.ui.layout.RowScope,kotlin.Unit> content);
   }
 
+  public enum BottomDrawerState {
+    method public static androidx.ui.material.BottomDrawerState valueOf(String name) throws java.lang.IllegalArgumentException;
+    method public static androidx.ui.material.BottomDrawerState[] values();
+    enum_constant public static final androidx.ui.material.BottomDrawerState Closed;
+    enum_constant public static final androidx.ui.material.BottomDrawerState Expanded;
+    enum_constant public static final androidx.ui.material.BottomDrawerState Opened;
+  }
+
   public final class BottomNavigationKt {
     method @androidx.compose.Composable public static void BottomNavigation-oP-1cd0(androidx.ui.core.Modifier modifier = Modifier, long backgroundColor = MaterialTheme.colors.primarySurface, long contentColor = contentColorFor(backgroundColor), float elevation = androidx.ui.material.BottomNavigationKt.BottomNavigationElevation, kotlin.jvm.functions.Function1<? super androidx.ui.layout.RowScope,kotlin.Unit> content);
     method @androidx.compose.Composable public static void BottomNavigationItem-dOPBtLY(kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> text = emptyContent(), boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, androidx.ui.core.Modifier modifier = Modifier, boolean alwaysShowLabels = true, long activeColor = contentColor(), long inactiveColor = EmphasisAmbient.current.medium.applyEmphasis(activeColor));
@@ -104,7 +112,7 @@
   }
 
   public final class DrawerKt {
-    method @androidx.compose.Composable public static void BottomDrawerLayout-s-rmCOo(androidx.ui.material.DrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.DrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
+    method @androidx.compose.Composable public static void BottomDrawerLayout-8vp1Knk(androidx.ui.material.BottomDrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.BottomDrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
     method @androidx.compose.Composable public static void ModalDrawerLayout-s-rmCOo(androidx.ui.material.DrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.DrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
   }
 
diff --git a/ui/ui-material/api/public_plus_experimental_0.1.0-dev15.txt b/ui/ui-material/api/public_plus_experimental_0.1.0-dev15.txt
index 289b9e3..6b26c47 100644
--- a/ui/ui-material/api/public_plus_experimental_0.1.0-dev15.txt
+++ b/ui/ui-material/api/public_plus_experimental_0.1.0-dev15.txt
@@ -19,6 +19,14 @@
     method @androidx.compose.Composable public static void TopAppBar-oP-1cd0(androidx.ui.core.Modifier modifier = Modifier, long backgroundColor = MaterialTheme.colors.primarySurface, long contentColor = contentColorFor(backgroundColor), float elevation = androidx.ui.material.AppBarKt.TopAppBarElevation, kotlin.jvm.functions.Function1<? super androidx.ui.layout.RowScope,kotlin.Unit> content);
   }
 
+  public enum BottomDrawerState {
+    method public static androidx.ui.material.BottomDrawerState valueOf(String name) throws java.lang.IllegalArgumentException;
+    method public static androidx.ui.material.BottomDrawerState[] values();
+    enum_constant public static final androidx.ui.material.BottomDrawerState Closed;
+    enum_constant public static final androidx.ui.material.BottomDrawerState Expanded;
+    enum_constant public static final androidx.ui.material.BottomDrawerState Opened;
+  }
+
   public final class BottomNavigationKt {
     method @androidx.compose.Composable public static void BottomNavigation-oP-1cd0(androidx.ui.core.Modifier modifier = Modifier, long backgroundColor = MaterialTheme.colors.primarySurface, long contentColor = contentColorFor(backgroundColor), float elevation = androidx.ui.material.BottomNavigationKt.BottomNavigationElevation, kotlin.jvm.functions.Function1<? super androidx.ui.layout.RowScope,kotlin.Unit> content);
     method @androidx.compose.Composable public static void BottomNavigationItem-dOPBtLY(kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> text = emptyContent(), boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, androidx.ui.core.Modifier modifier = Modifier, boolean alwaysShowLabels = true, long activeColor = contentColor(), long inactiveColor = EmphasisAmbient.current.medium.applyEmphasis(activeColor));
@@ -104,7 +112,7 @@
   }
 
   public final class DrawerKt {
-    method @androidx.compose.Composable public static void BottomDrawerLayout-s-rmCOo(androidx.ui.material.DrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.DrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
+    method @androidx.compose.Composable public static void BottomDrawerLayout-8vp1Knk(androidx.ui.material.BottomDrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.BottomDrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
     method @androidx.compose.Composable public static void ModalDrawerLayout-s-rmCOo(androidx.ui.material.DrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.DrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
   }
 
diff --git a/ui/ui-material/api/public_plus_experimental_current.txt b/ui/ui-material/api/public_plus_experimental_current.txt
index 289b9e3..6b26c47 100644
--- a/ui/ui-material/api/public_plus_experimental_current.txt
+++ b/ui/ui-material/api/public_plus_experimental_current.txt
@@ -19,6 +19,14 @@
     method @androidx.compose.Composable public static void TopAppBar-oP-1cd0(androidx.ui.core.Modifier modifier = Modifier, long backgroundColor = MaterialTheme.colors.primarySurface, long contentColor = contentColorFor(backgroundColor), float elevation = androidx.ui.material.AppBarKt.TopAppBarElevation, kotlin.jvm.functions.Function1<? super androidx.ui.layout.RowScope,kotlin.Unit> content);
   }
 
+  public enum BottomDrawerState {
+    method public static androidx.ui.material.BottomDrawerState valueOf(String name) throws java.lang.IllegalArgumentException;
+    method public static androidx.ui.material.BottomDrawerState[] values();
+    enum_constant public static final androidx.ui.material.BottomDrawerState Closed;
+    enum_constant public static final androidx.ui.material.BottomDrawerState Expanded;
+    enum_constant public static final androidx.ui.material.BottomDrawerState Opened;
+  }
+
   public final class BottomNavigationKt {
     method @androidx.compose.Composable public static void BottomNavigation-oP-1cd0(androidx.ui.core.Modifier modifier = Modifier, long backgroundColor = MaterialTheme.colors.primarySurface, long contentColor = contentColorFor(backgroundColor), float elevation = androidx.ui.material.BottomNavigationKt.BottomNavigationElevation, kotlin.jvm.functions.Function1<? super androidx.ui.layout.RowScope,kotlin.Unit> content);
     method @androidx.compose.Composable public static void BottomNavigationItem-dOPBtLY(kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> text = emptyContent(), boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, androidx.ui.core.Modifier modifier = Modifier, boolean alwaysShowLabels = true, long activeColor = contentColor(), long inactiveColor = EmphasisAmbient.current.medium.applyEmphasis(activeColor));
@@ -104,7 +112,7 @@
   }
 
   public final class DrawerKt {
-    method @androidx.compose.Composable public static void BottomDrawerLayout-s-rmCOo(androidx.ui.material.DrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.DrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
+    method @androidx.compose.Composable public static void BottomDrawerLayout-8vp1Knk(androidx.ui.material.BottomDrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.BottomDrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
     method @androidx.compose.Composable public static void ModalDrawerLayout-s-rmCOo(androidx.ui.material.DrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.DrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
   }
 
diff --git a/ui/ui-material/api/restricted_0.1.0-dev15.txt b/ui/ui-material/api/restricted_0.1.0-dev15.txt
index 381198f..dbfe1d3 100644
--- a/ui/ui-material/api/restricted_0.1.0-dev15.txt
+++ b/ui/ui-material/api/restricted_0.1.0-dev15.txt
@@ -19,6 +19,14 @@
     method @androidx.compose.Composable public static void TopAppBar-oP-1cd0(androidx.ui.core.Modifier modifier = Modifier, long backgroundColor = MaterialTheme.colors.primarySurface, long contentColor = contentColorFor(backgroundColor), float elevation = androidx.ui.material.AppBarKt.TopAppBarElevation, kotlin.jvm.functions.Function1<? super androidx.ui.layout.RowScope,kotlin.Unit> content);
   }
 
+  public enum BottomDrawerState {
+    method public static androidx.ui.material.BottomDrawerState valueOf(String name) throws java.lang.IllegalArgumentException;
+    method public static androidx.ui.material.BottomDrawerState[] values();
+    enum_constant public static final androidx.ui.material.BottomDrawerState Closed;
+    enum_constant public static final androidx.ui.material.BottomDrawerState Expanded;
+    enum_constant public static final androidx.ui.material.BottomDrawerState Opened;
+  }
+
   public final class BottomNavigationKt {
     method @androidx.compose.Composable public static void BottomNavigation-oP-1cd0(androidx.ui.core.Modifier modifier = Modifier, long backgroundColor = MaterialTheme.colors.primarySurface, long contentColor = contentColorFor(backgroundColor), float elevation = androidx.ui.material.BottomNavigationKt.BottomNavigationElevation, kotlin.jvm.functions.Function1<? super androidx.ui.layout.RowScope,kotlin.Unit> content);
     method @androidx.compose.Composable public static void BottomNavigationItem-dOPBtLY(kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> text = emptyContent(), boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, androidx.ui.core.Modifier modifier = Modifier, boolean alwaysShowLabels = true, long activeColor = contentColor(), long inactiveColor = EmphasisAmbient.current.medium.applyEmphasis(activeColor));
@@ -105,7 +113,7 @@
   }
 
   public final class DrawerKt {
-    method @androidx.compose.Composable public static void BottomDrawerLayout-s-rmCOo(androidx.ui.material.DrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.DrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
+    method @androidx.compose.Composable public static void BottomDrawerLayout-8vp1Knk(androidx.ui.material.BottomDrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.BottomDrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
     method @androidx.compose.Composable public static void ModalDrawerLayout-s-rmCOo(androidx.ui.material.DrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.DrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
   }
 
diff --git a/ui/ui-material/api/restricted_current.txt b/ui/ui-material/api/restricted_current.txt
index 381198f..dbfe1d3 100644
--- a/ui/ui-material/api/restricted_current.txt
+++ b/ui/ui-material/api/restricted_current.txt
@@ -19,6 +19,14 @@
     method @androidx.compose.Composable public static void TopAppBar-oP-1cd0(androidx.ui.core.Modifier modifier = Modifier, long backgroundColor = MaterialTheme.colors.primarySurface, long contentColor = contentColorFor(backgroundColor), float elevation = androidx.ui.material.AppBarKt.TopAppBarElevation, kotlin.jvm.functions.Function1<? super androidx.ui.layout.RowScope,kotlin.Unit> content);
   }
 
+  public enum BottomDrawerState {
+    method public static androidx.ui.material.BottomDrawerState valueOf(String name) throws java.lang.IllegalArgumentException;
+    method public static androidx.ui.material.BottomDrawerState[] values();
+    enum_constant public static final androidx.ui.material.BottomDrawerState Closed;
+    enum_constant public static final androidx.ui.material.BottomDrawerState Expanded;
+    enum_constant public static final androidx.ui.material.BottomDrawerState Opened;
+  }
+
   public final class BottomNavigationKt {
     method @androidx.compose.Composable public static void BottomNavigation-oP-1cd0(androidx.ui.core.Modifier modifier = Modifier, long backgroundColor = MaterialTheme.colors.primarySurface, long contentColor = contentColorFor(backgroundColor), float elevation = androidx.ui.material.BottomNavigationKt.BottomNavigationElevation, kotlin.jvm.functions.Function1<? super androidx.ui.layout.RowScope,kotlin.Unit> content);
     method @androidx.compose.Composable public static void BottomNavigationItem-dOPBtLY(kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> text = emptyContent(), boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, androidx.ui.core.Modifier modifier = Modifier, boolean alwaysShowLabels = true, long activeColor = contentColor(), long inactiveColor = EmphasisAmbient.current.medium.applyEmphasis(activeColor));
@@ -105,7 +113,7 @@
   }
 
   public final class DrawerKt {
-    method @androidx.compose.Composable public static void BottomDrawerLayout-s-rmCOo(androidx.ui.material.DrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.DrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
+    method @androidx.compose.Composable public static void BottomDrawerLayout-8vp1Knk(androidx.ui.material.BottomDrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.BottomDrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
     method @androidx.compose.Composable public static void ModalDrawerLayout-s-rmCOo(androidx.ui.material.DrawerState drawerState, kotlin.jvm.functions.Function1<? super androidx.ui.material.DrawerState,kotlin.Unit> onStateChange, boolean gesturesEnabled = true, androidx.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
   }
 
diff --git a/ui/ui-material/samples/src/main/java/androidx/ui/material/samples/DrawerSamples.kt b/ui/ui-material/samples/src/main/java/androidx/ui/material/samples/DrawerSamples.kt
index 51431bb..f49012c 100644
--- a/ui/ui-material/samples/src/main/java/androidx/ui/material/samples/DrawerSamples.kt
+++ b/ui/ui-material/samples/src/main/java/androidx/ui/material/samples/DrawerSamples.kt
@@ -29,6 +29,7 @@
 import androidx.ui.layout.fillMaxSize
 import androidx.ui.layout.preferredHeight
 import androidx.ui.material.BottomDrawerLayout
+import androidx.ui.material.BottomDrawerState
 import androidx.ui.material.Button
 import androidx.ui.material.DrawerState
 import androidx.ui.material.ModalDrawerLayout
@@ -39,36 +40,52 @@
 fun ModalDrawerSample() {
     val (state, onStateChange) = state { DrawerState.Closed }
     val appContentText =
-        if (state == DrawerState.Closed) ">>> Pull to open >>>" else "<<< Swipe to close <<<"
+        if (state == DrawerState.Closed) {
+            ">>> Pull to open >>>"
+        } else {
+            "<<< Swipe to close <<<"
+        }
     ModalDrawerLayout(
         drawerState = state,
         >
-        drawerContent = { YourDrawerContent(onStateChange) },
-        bodyContent = { YourAppContent(appContentText, onStateChange) }
+        drawerContent = {
+            YourDrawerContent( onStateChange(DrawerState.Closed) })
+        },
+        bodyContent = {
+            YourAppContent(appContentText,  onStateChange(DrawerState.Opened) })
+        }
     )
 }
 
 @Sampled
 @Composable
 fun BottomDrawerSample() {
-    val (state, onStateChange) = state { DrawerState.Closed }
+    val (state, onStateChange) = state { BottomDrawerState.Closed }
     val appContentText =
-        if (state == DrawerState.Closed) "▲▲▲ Pull to open ▲▲▲" else "▼▼▼ Drag down to close ▼▼▼"
+        if (state == BottomDrawerState.Closed) {
+            "▲▲▲ Pull to open ▲▲▲"
+        } else {
+            "▼▼▼ Drag down to close ▼▼▼"
+        }
     BottomDrawerLayout(
         drawerState = state,
         >
-        drawerContent = { YourDrawerContent(onStateChange) },
-        bodyContent = { YourAppContent(appContentText, onStateChange) }
+        drawerContent = {
+            YourDrawerContent( onStateChange(BottomDrawerState.Closed) })
+        },
+        bodyContent = {
+            YourAppContent(appContentText,  onStateChange(BottomDrawerState.Opened) })
+        }
     )
 }
 
 @Composable
-private fun YourDrawerContent(onStateChange: (DrawerState) -> Unit) {
+private fun YourDrawerContent(onClose: () -> Unit) {
     Box(Modifier.fillMaxSize(), gravity = ContentGravity.Center) {
         Column(Modifier.fillMaxHeight()) {
             Text(text = "Drawer Content")
             Spacer(Modifier.preferredHeight(20.dp))
-            Button( onStateChange(DrawerState.Closed) }) {
+            Button( {
                 Text("Close Drawer")
             }
         }
@@ -76,11 +93,11 @@
 }
 
 @Composable
-private fun YourAppContent(text: String, onDrawerStateChange: (DrawerState) -> Unit) {
+private fun YourAppContent(text: String, onOpen: () -> Unit) {
     Column(Modifier.fillMaxHeight()) {
         Text(text = text)
         Spacer(Modifier.preferredHeight(20.dp))
-        Button( onDrawerStateChange(DrawerState.Opened) }) {
+        Button( {
             Text("Click to open")
         }
     }
diff --git a/ui/ui-material/src/androidTest/java/androidx/ui/material/DrawerScreenshotTest.kt b/ui/ui-material/src/androidTest/java/androidx/ui/material/DrawerScreenshotTest.kt
index 5a21b05..57d6209 100644
--- a/ui/ui-material/src/androidTest/java/androidx/ui/material/DrawerScreenshotTest.kt
+++ b/ui/ui-material/src/androidTest/java/androidx/ui/material/DrawerScreenshotTest.kt
@@ -48,7 +48,7 @@
     @get:Rule
     val screenshotRule = AndroidXScreenshotTestRule(GOLDEN_MATERIAL)
 
-    private fun ComposeTestRule.setBottomDrawer(drawerState: DrawerState) {
+    private fun ComposeTestRule.setBottomDrawer(drawerState: BottomDrawerState) {
         setMaterialContent {
             Box(Modifier.size(10.dp, 100.dp).testTag("container")) {
                 BottomDrawerLayout(
@@ -76,7 +76,7 @@
 
     @Test
     fun bottomDrawer_closed() {
-        composeTestRule.setBottomDrawer(DrawerState.Closed)
+        composeTestRule.setBottomDrawer(BottomDrawerState.Closed)
         assertScreenshotAgainstGolden("bottomDrawer_closed")
     }
 
@@ -88,7 +88,7 @@
 
     @Test
     fun bottomDrawer_opened() {
-        composeTestRule.setBottomDrawer(DrawerState.Opened)
+        composeTestRule.setBottomDrawer(BottomDrawerState.Opened)
         assertScreenshotAgainstGolden("bottomDrawer_opened")
     }
 
diff --git a/ui/ui-material/src/androidTest/java/androidx/ui/material/DrawerTest.kt b/ui/ui-material/src/androidTest/java/androidx/ui/material/DrawerTest.kt
index 510382b..1e6e627 100644
--- a/ui/ui-material/src/androidTest/java/androidx/ui/material/DrawerTest.kt
+++ b/ui/ui-material/src/androidTest/java/androidx/ui/material/DrawerTest.kt
@@ -31,10 +31,12 @@
 import androidx.ui.graphics.Color
 import androidx.ui.layout.fillMaxSize
 import androidx.ui.layout.rtl
+import androidx.ui.test.GestureScope
 import androidx.ui.test.assertIsEqualTo
 import androidx.ui.test.assertLeftPositionInRootIsEqualTo
 import androidx.ui.test.assertTopPositionInRootIsEqualTo
 import androidx.ui.test.assertWidthIsEqualTo
+import androidx.ui.test.center
 import androidx.ui.test.createComposeRule
 import androidx.ui.test.doGesture
 import androidx.ui.test.findByTag
@@ -42,6 +44,7 @@
 import androidx.ui.test.runOnIdleCompose
 import androidx.ui.test.runOnUiThread
 import androidx.ui.test.sendClick
+import androidx.ui.test.sendSwipe
 import androidx.ui.test.sendSwipeDown
 import androidx.ui.test.sendSwipeLeft
 import androidx.ui.test.sendSwipeRight
@@ -110,7 +113,7 @@
     @Test
     fun bottomDrawer_testOffset_whenOpened() {
         composeTestRule.setMaterialContent {
-            BottomDrawerLayout(DrawerState.Opened, {}, drawerContent = {
+            BottomDrawerLayout(BottomDrawerState.Opened, {}, drawerContent = {
                 Box(Modifier.fillMaxSize().testTag("content"))
             }, bodyContent = emptyContent())
         }
@@ -126,7 +129,7 @@
     fun bottomDrawer_testOffset_whenClosed() {
         var position: Offset? = null
         composeTestRule.setMaterialContent {
-            BottomDrawerLayout(DrawerState.Closed, {}, drawerContent = {
+            BottomDrawerLayout(BottomDrawerState.Closed, {}, drawerContent = {
                 Box(Modifier.fillMaxSize().onPositioned { coords: LayoutCoordinates ->
                     position = coords.localToRoot(Offset.Zero)
                 })
@@ -240,7 +243,7 @@
         var openedHeight: Int? = null
         var openedLatch: CountDownLatch? = null
         var closedLatch: CountDownLatch? = CountDownLatch(1)
-        val drawerState = mutableStateOf(DrawerState.Closed)
+        val drawerState = mutableStateOf(BottomDrawerState.Closed)
         composeTestRule.setMaterialContent {
             BottomDrawerLayout(drawerState.value, { drawerState.value = it },
                 drawerContent = {
@@ -269,7 +272,7 @@
         // When the drawer state is set to Opened
         openedLatch = CountDownLatch(1)
         runOnIdleCompose {
-            drawerState.value = DrawerState.Opened
+            drawerState.value = BottomDrawerState.Opened
         }
         // Then the drawer should be opened
         assertThat(openedLatch.await(5, TimeUnit.SECONDS)).isTrue()
@@ -277,7 +280,7 @@
         // When the drawer state is set to Closed
         closedLatch = CountDownLatch(1)
         runOnIdleCompose {
-            drawerState.value = DrawerState.Closed
+            drawerState.value = BottomDrawerState.Closed
         }
         // Then the drawer should be closed
         assertThat(closedLatch.await(5, TimeUnit.SECONDS)).isTrue()
@@ -287,7 +290,7 @@
     fun bottomDrawer_bodyContent_clickable() {
         var drawerClicks = 0
         var bodyClicks = 0
-        val drawerState = mutableStateOf(DrawerState.Closed)
+        val drawerState = mutableStateOf(BottomDrawerState.Closed)
         composeTestRule.setMaterialContent {
             // emulate click on the screen
             BottomDrawerLayout(drawerState.value, { drawerState.value = it },
@@ -314,7 +317,7 @@
         }
 
         runOnUiThread {
-            drawerState.value = DrawerState.Opened
+            drawerState.value = BottomDrawerState.Opened
         }
         sleep(100) // TODO(147586311): remove this sleep when opening the drawer triggers a wait
 
@@ -394,7 +397,7 @@
 
     @Test
     fun bottomDrawer_openBySwipe() {
-        val drawerState = mutableStateOf(DrawerState.Closed)
+        val drawerState = mutableStateOf(BottomDrawerState.Closed)
         composeTestRule.setMaterialContent {
             // emulate click on the screen
             Box(Modifier.testTag("Drawer")) {
@@ -412,14 +415,96 @@
             .doGesture { sendSwipeUp() }
 
         runOnIdleCompose {
-            assertThat(drawerState.value).isEqualTo(DrawerState.Opened)
+            assertThat(drawerState.value).isEqualTo(BottomDrawerState.Expanded)
         }
 
         findByTag("Drawer")
             .doGesture { sendSwipeDown() }
 
         runOnIdleCompose {
-            assertThat(drawerState.value).isEqualTo(DrawerState.Closed)
+            assertThat(drawerState.value).isEqualTo(BottomDrawerState.Closed)
         }
     }
+
+    @Test
+    fun bottomDrawer_openBySwipe_thresholds() {
+        val drawerState = mutableStateOf(BottomDrawerState.Closed)
+        composeTestRule.setMaterialContent {
+            // emulate click on the screen
+            Box(Modifier.testTag("Drawer")) {
+                BottomDrawerLayout(drawerState.value, { drawerState.value = it },
+                    drawerContent = {
+                        Box(Modifier.fillMaxSize().drawBackground(Color.Magenta))
+                    },
+                    bodyContent = {
+                        Box(Modifier.fillMaxSize().drawBackground(Color.Red))
+                    })
+            }
+        }
+        val threshold = with (composeTestRule.density) { BottomDrawerThreshold.toPx() }
+
+        findByTag("Drawer")
+            .doGesture { sendSwipeUpBy(threshold / 2) }
+
+        runOnIdleCompose {
+            assertThat(drawerState.value).isEqualTo(BottomDrawerState.Closed)
+        }
+
+        findByTag("Drawer")
+            .doGesture { sendSwipeUpBy(threshold) }
+
+        runOnIdleCompose {
+            assertThat(drawerState.value).isEqualTo(BottomDrawerState.Opened)
+        }
+
+        findByTag("Drawer")
+            .doGesture { sendSwipeUpBy(threshold / 2) }
+
+        runOnIdleCompose {
+            assertThat(drawerState.value).isEqualTo(BottomDrawerState.Opened)
+        }
+
+        findByTag("Drawer")
+            .doGesture { sendSwipeUpBy(threshold) }
+
+        runOnIdleCompose {
+            assertThat(drawerState.value).isEqualTo(BottomDrawerState.Expanded)
+        }
+
+        findByTag("Drawer")
+            .doGesture { sendSwipeDownBy(threshold / 2) }
+
+        runOnIdleCompose {
+            assertThat(drawerState.value).isEqualTo(BottomDrawerState.Expanded)
+        }
+
+        findByTag("Drawer")
+            .doGesture { sendSwipeDownBy(threshold) }
+
+        runOnIdleCompose {
+            assertThat(drawerState.value).isEqualTo(BottomDrawerState.Opened)
+        }
+
+        findByTag("Drawer")
+            .doGesture { sendSwipeDownBy(threshold / 2) }
+
+        runOnIdleCompose {
+            assertThat(drawerState.value).isEqualTo(BottomDrawerState.Opened)
+        }
+
+        findByTag("Drawer")
+            .doGesture { sendSwipeDownBy(threshold) }
+
+        runOnIdleCompose {
+            assertThat(drawerState.value).isEqualTo(BottomDrawerState.Closed)
+        }
+    }
+
+    private fun GestureScope.sendSwipeUpBy(offset: Float) {
+        sendSwipe(center, center.copy(y = center.y - offset))
+    }
+
+    private fun GestureScope.sendSwipeDownBy(offset: Float) {
+        sendSwipe(center, center.copy(y = center.y + offset))
+    }
 }
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/Drawer.kt b/ui/ui-material/src/main/java/androidx/ui/material/Drawer.kt
index 2f4b01c..7b9d87e 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/Drawer.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/Drawer.kt
@@ -35,13 +35,14 @@
 import androidx.ui.layout.offsetPx
 import androidx.ui.layout.padding
 import androidx.ui.layout.preferredSizeIn
+import androidx.ui.material.internal.fixedThresholds
 import androidx.ui.material.internal.stateDraggable
 import androidx.ui.unit.Dp
 import androidx.ui.unit.dp
 import androidx.ui.util.lerp
 
 /**
- * Possible states of the drawer
+ * Possible states of the [ModalDrawerLayout]
  */
 enum class DrawerState {
     /**
@@ -51,8 +52,25 @@
     /**
      * Constant to indicate the state of the drawer when it's opened
      */
+    Opened
+}
+
+/**
+ * Possible states of the [BottomDrawerLayout]
+ */
+enum class BottomDrawerState {
+    /**
+     * Constant to indicate the state of the bottom drawer when it's closed
+     */
+    Closed,
+    /**
+     * Constant to indicate the state of the bottom drawer when it's opened
+     */
     Opened,
-    // Expanded
+    /**
+     * Constant to indicate the state of the bottom drawer when it's fully expanded
+     */
+    Expanded
 }
 
 /**
@@ -117,9 +135,11 @@
             Stack {
                 bodyContent()
             }
-            Scrim(drawerState, onStateChange, fraction = {
-                calculateFraction(minValue, maxValue, drawerPosition.value)
-            })
+            Scrim(
+                opened = drawerState == DrawerState.Opened,
+                 onStateChange(DrawerState.Closed) },
+                fraction = { calculateFraction(minValue, maxValue, drawerPosition.value) }
+            )
             DrawerContent(
                 drawerPosition, dpConstraints, drawerShape, drawerElevation, drawerContent
             )
@@ -155,8 +175,8 @@
  */
 @Composable
 fun BottomDrawerLayout(
-    drawerState: DrawerState,
-    onStateChange: (DrawerState) -> Unit,
+    drawerState: BottomDrawerState,
+    onStateChange: (BottomDrawerState) -> Unit,
     gesturesEnabled: Boolean = true,
     drawerShape: Shape = MaterialTheme.shapes.large,
     drawerElevation: Dp = DrawerConstants.DefaultElevation,
@@ -183,20 +203,25 @@
         )
         val anchors =
             if (isLandscape) {
-                listOf(maxValue to DrawerState.Closed, minValue to DrawerState.Opened)
+                listOf(
+                    maxValue to BottomDrawerState.Closed,
+                    minValue to BottomDrawerState.Opened
+                )
             } else {
                 listOf(
-                    maxValue to DrawerState.Closed,
-                    openedValue to DrawerState.Opened,
-                    minValue to DrawerState.Opened
+                    maxValue to BottomDrawerState.Closed,
+                    openedValue to BottomDrawerState.Opened,
+                    minValue to BottomDrawerState.Expanded
                 )
             }
         val drawerPosition = state { maxValue }
+        val offset = with(DensityAmbient.current) { BottomDrawerThreshold.toPx() }
         Stack(
             Modifier.stateDraggable(
                 state = drawerState,
                 >
                 anchorsToState = anchors,
+                thresholds = fixedThresholds(offset),
                 animationSpec = AnimationSpec,
                 dragDirection = DragDirection.Vertical,
                 minValue = minValue,
@@ -208,10 +233,14 @@
             Stack {
                 bodyContent()
             }
-            Scrim(drawerState, onStateChange, fraction = {
-                // as we scroll "from height to 0" , need to reverse fraction
-                1 - calculateFraction(openedValue, maxValue, drawerPosition.value)
-            })
+            Scrim(
+                opened = drawerState == BottomDrawerState.Opened,
+                 onStateChange(BottomDrawerState.Closed) },
+                fraction = {
+                    // as we scroll "from height to 0" , need to reverse fraction
+                    1 - calculateFraction(openedValue, maxValue, drawerPosition.value)
+                }
+            )
             BottomDrawerContent(
                 drawerPosition, dpConstraints, drawerShape, drawerElevation, drawerContent
             )
@@ -275,13 +304,13 @@
 
 @Composable
 private fun Scrim(
-    state: DrawerState,
-    onStateChange: (DrawerState) -> Unit,
+    opened: Boolean,
+    onClose: () -> Unit,
     fraction: () -> Float
 ) {
     val color = MaterialTheme.colors.onSurface
-    val dismissDrawer = if (state == DrawerState.Opened) {
-        Modifier.tapGestureFilter { _ -> onStateChange(DrawerState.Closed) }
+    val dismissDrawer = if (opened) {
+        Modifier.tapGestureFilter { onClose() }
     } else {
         Modifier
     }
@@ -303,3 +332,4 @@
 private val AnimationSpec = SpringSpec<Float>(stiffness = DrawerStiffness)
 
 internal const val BottomDrawerOpenFraction = 0.5f
+internal val BottomDrawerThreshold = 56.dp
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/internal/StateDraggable.kt b/ui/ui-material/src/main/java/androidx/ui/material/internal/StateDraggable.kt
index ebe8b9f..8f6f731 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/internal/StateDraggable.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/internal/StateDraggable.kt
@@ -20,7 +20,10 @@
 import androidx.animation.AnimationClockObservable
 import androidx.animation.AnimationEndReason
 import androidx.animation.AnimationSpec
+import androidx.animation.ExponentialDecay
 import androidx.animation.Spring
+import androidx.animation.TargetAnimation
+import androidx.annotation.FloatRange
 import androidx.compose.onCommit
 import androidx.compose.remember
 import androidx.compose.state
@@ -29,11 +32,13 @@
 import androidx.ui.core.Modifier
 import androidx.ui.core.composed
 import androidx.ui.foundation.InteractionState
-import androidx.ui.foundation.animation.AnchorsFlingConfig
+import androidx.ui.foundation.animation.FlingConfig
 import androidx.ui.foundation.animation.fling
 import androidx.ui.foundation.gestures.DragDirection
 import androidx.ui.foundation.gestures.draggable
 import androidx.ui.util.fastFirstOrNull
+import androidx.ui.util.lerp
+import kotlin.math.sign
 
 /**
  * Enable automatic drag and animation between predefined states.
@@ -57,6 +62,9 @@
  * @param animationSpec animation which will be used for animations
  * @param dragDirection direction in which drag should be happening.
  * Either [DragDirection.Vertical] or [DragDirection.Horizontal]
+ * @param thresholds the thresholds between anchors that determine which anchor to fling to when
+ * dragging stops, represented as a lambda that takes a pair of anchors and returns a value
+ * between them (note that the order of the anchors matters as it indicates the drag direction)
  * @param enabled whether or not this Draggable is enabled and should consume events
  * @param minValue lower bound for draggable value in this component
  * @param maxValue upper bound for draggable value in this component
@@ -69,6 +77,7 @@
     anchorsToState: List<Pair<Float, T>>,
     animationSpec: AnimationSpec<Float>,
     dragDirection: DragDirection,
+    thresholds: (Float, Float) -> Float = fractionalThresholds(0.5f),
     enabled: Boolean = true,
     minValue: Float = Float.MIN_VALUE,
     maxValue: Float = Float.MAX_VALUE,
@@ -79,8 +88,9 @@
 
     val anchors = remember(anchorsToState) { anchorsToState.map { it.first } }
     val currentValue = anchorsToState.fastFirstOrNull { it.second == state }!!.first
-    val flingConfig =
-        AnchorsFlingConfig(anchors, animationSpec,  reason, finalValue, _ ->
+    val flingConfig = FlingConfig(
+        decayAnimation = ExponentialDecay(),
+         reason, finalValue, _ ->
             if (reason != AnimationEndReason.Interrupted) {
                 val newState = anchorsToState.firstOrNull { it.first == finalValue }?.second
                 if (newState != null && newState != state) {
@@ -88,7 +98,41 @@
                     forceAnimationCheck.value = !forceAnimationCheck.value
                 }
             }
-        })
+        },
+        adjustTarget = { target ->
+            // Find the two anchors the target lies between.
+            val a = anchors.filter { it <= target }.max()
+            val b = anchors.filter { it >= target }.min()
+            // Compute which anchor to fling to.
+            val adjusted: Float =
+                if (a == null && b == null) {
+                    // There are no anchors, so return the target unchanged.
+                    target
+                } else if (a == null) {
+                    // The target lies below the anchors, so return the first anchor (b).
+                    b!!
+                } else if (b == null) {
+                    // The target lies above the anchors, so return the last anchor (b).
+                    a
+                } else if (a == b) {
+                    // The target is equal to one of the anchors, so return the target unchanged.
+                    target
+                } else {
+                    // The target lies strictly between the two anchors a and b.
+                    // Compute the threshold between a and b based on the drag direction.
+                    val threshold = if (currentValue <= a) {
+                        thresholds(a, b)
+                    } else {
+                        thresholds(b, a)
+                    }
+                    require(threshold >= a && threshold <= b) {
+                        "Invalid threshold $threshold between anchors $a and $b."
+                    }
+                    if (target < threshold) a else b
+                }
+            TargetAnimation(adjusted, animationSpec)
+        }
+    )
     val clock = AnimationClockAmbient.current.asDisposableClock()
     val position = remember(clock) {
         onNewValue(currentValue)
@@ -116,6 +160,19 @@
     )
 }
 
+/**
+ * Fixed anchors thresholds. Each threshold will be at an [offset] away from the first anchor.
+ */
+internal fun fixedThresholds(offset: Float): (Float, Float) -> Float =
+    { fromAnchor, toAnchor -> fromAnchor + offset * sign(toAnchor - fromAnchor) }
+
+/**
+ * Fractional thresholds. Each threshold will be at a [fraction] of the way between the two anchors.
+ */
+internal fun fractionalThresholds(
+    @FloatRange(from = 0.0, to = 1.0) fraction: Float
+): (Float, Float) -> Float = { fromAnchor, toAnchor -> lerp(fromAnchor, toAnchor, fraction) }
+
 private class NotificationBasedAnimatedFloat(
     initial: Float,
     clock: AnimationClockObservable,