Merge "Performance improvements for measure pass." into androidx-master-dev am: dda4893b77

Change-Id: I4df26291f5f46c3f27544efa31ac78102e5315e5
diff --git a/ui/ui-animation-core/src/main/java/androidx/animation/TransitionDefinition.kt b/ui/ui-animation-core/src/main/java/androidx/animation/TransitionDefinition.kt
index b9a83b6..ce0c1e3a 100644
--- a/ui/ui-animation-core/src/main/java/androidx/animation/TransitionDefinition.kt
+++ b/ui/ui-animation-core/src/main/java/androidx/animation/TransitionDefinition.kt
@@ -16,6 +16,7 @@
 
 package androidx.animation
 
+import androidx.ui.util.fastFirstOrNull
 import kotlin.experimental.ExperimentalTypeInference
 
 /**
@@ -269,10 +270,10 @@
         }
 
     internal fun getSpec(fromState: T, toState: T): TransitionSpec<T> {
-        return transitionSpecs.firstOrNull { it.defines(fromState, toState) }
-            ?: transitionSpecs.firstOrNull { it.defines(fromState, null) }
-            ?: transitionSpecs.firstOrNull { it.defines(null, toState) }
-            ?: transitionSpecs.firstOrNull { it.defines(null, null) }
+        return transitionSpecs.fastFirstOrNull { it.defines(fromState, toState) }
+            ?: transitionSpecs.fastFirstOrNull { it.defines(fromState, null) }
+            ?: transitionSpecs.fastFirstOrNull { it.defines(null, toState) }
+            ?: transitionSpecs.fastFirstOrNull { it.defines(null, null) }
             ?: defaultTransitionSpec
     }
 
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/ComponentNodes.kt b/ui/ui-core/src/main/java/androidx/ui/core/ComponentNodes.kt
index 1984062..32f1d67 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/ComponentNodes.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/ComponentNodes.kt
@@ -680,6 +680,19 @@
     private var outerZIndexModifier: ZIndexModifier? = null
 
     /**
+     * The inner-most layer wrapper. Used for performance for LayoutNodeWrapper.findLayer().
+     */
+    internal var innerLayerWrapper: LayerWrapper? = null
+
+    /**
+     * Returns the inner-most layer as part of this LayoutNode or from the containing LayoutNode.
+     * This is added for performance so that LayoutNodeWrapper.findLayer() can be faster.
+     */
+    internal fun findLayer(): OwnedLayer? {
+        return innerLayerWrapper?.layer ?: parentLayoutNode?.findLayer()
+    }
+
+    /**
      * The [Modifier] currently applied to this node.
      */
     var modifier: Modifier = Modifier
@@ -693,6 +706,7 @@
             onPositionedCallbacks.clear()
             onChildPositionedCallbacks.clear()
             outerZIndexModifier = null
+            innerLayerWrapper = null
             layoutNodeWrapper = modifier.foldOut(innerLayoutNodeWrapper) { mod, toWrap ->
                 var wrapper = toWrap
                 // The order in which the following blocks occur matters.  For example, the
@@ -709,7 +723,11 @@
                     wrapper = ModifiedDrawNode(wrapper, mod)
                 }
                 if (mod is DrawLayerModifier) {
-                    wrapper = LayerWrapper(wrapper, mod)
+                    val layerWrapper = LayerWrapper(wrapper, mod)
+                    wrapper = layerWrapper
+                    if (innerLayerWrapper == null) {
+                        innerLayerWrapper = layerWrapper
+                    }
                 }
                 if (mod is FocusModifier) {
                     require(mod is FocusModifierImpl)
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/InnerPlaceable.kt b/ui/ui-core/src/main/java/androidx/ui/core/InnerPlaceable.kt
index a99f63e..0e6d8ca 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/InnerPlaceable.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/InnerPlaceable.kt
@@ -28,6 +28,7 @@
 import androidx.ui.unit.PxPosition
 import androidx.ui.unit.toPxSize
 import androidx.ui.util.fastAny
+import androidx.ui.util.fastFirstOrNull
 import androidx.ui.util.fastForEach
 
 internal class InnerPlaceable(
@@ -62,7 +63,7 @@
             null
         } else {
             layoutNode.layoutChildren
-                    .firstOrNull { it.parentData != null }?.parentData
+                    .fastFirstOrNull { it.parentData != null }?.parentData
         }
 
     override fun findFocusWrapperWrappingThisWrapper() =
@@ -186,4 +187,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Layout.kt b/ui/ui-core/src/main/java/androidx/ui/core/Layout.kt
index ff348cc..47c2b40 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/Layout.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/Layout.kt
@@ -36,6 +36,7 @@
 import androidx.ui.unit.ipx
 import androidx.ui.unit.max
 import androidx.ui.unit.min
+import androidx.ui.util.fastForEach
 
 /**
  * [Layout] is the main core component for layout. It can be used to measure and position
@@ -199,7 +200,7 @@
             val width = placeables.maxBy { it.width }?.width ?: constraints.minWidth
             val height = placeables.maxBy { it.height }?.height ?: constraints.minHeight
             layout(width, height) {
-                placeables.forEach { it.place(IntPx.Zero, IntPx.Zero) }
+                placeables.fastForEach { it.place(IntPx.Zero, IntPx.Zero) }
             }
         }
         MeasuringIntrinsicsMeasureBlocks(measureBlock)
@@ -568,7 +569,7 @@
             val layoutChildren = root.layoutChildren
             var maxWidth: IntPx = constraints.minWidth
             var maxHeight: IntPx = constraints.minHeight
-            layoutChildren.forEach {
+            layoutChildren.fastForEach {
                 it.measure(constraints, layoutDirection)
                 maxWidth = max(maxWidth, it.width)
                 maxHeight = max(maxHeight, it.height)
@@ -577,7 +578,7 @@
             maxHeight = min(maxHeight, constraints.maxHeight)
 
             return measureScope.layout(maxWidth, maxHeight) {
-                layoutChildren.forEach { it.place(IntPx.Zero, IntPx.Zero) }
+                layoutChildren.fastForEach { it.place(IntPx.Zero, IntPx.Zero) }
             }
         }
     }
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/LayoutNodeWrapper.kt b/ui/ui-core/src/main/java/androidx/ui/core/LayoutNodeWrapper.kt
index bc2fe55..b660d948 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/LayoutNodeWrapper.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/LayoutNodeWrapper.kt
@@ -290,7 +290,13 @@
     /**
      * Returns the layer that this wrapper will draw into.
      */
-    open fun findLayer(): OwnedLayer? = wrappedBy?.findLayer()
+    open fun findLayer(): OwnedLayer? {
+        return if (layoutNode.innerLayerWrapper != null) {
+            wrappedBy?.findLayer()
+        } else {
+            layoutNode.findLayer()
+        }
+    }
 
     /**
      * Returns the first [ModifiedFocusNode] in the wrapper list that wraps this
diff --git a/ui/ui-core/src/main/java/androidx/ui/node/ViewInterop.kt b/ui/ui-core/src/main/java/androidx/ui/node/ViewInterop.kt
index 8985b93..7bd8279 100644
--- a/ui/ui-core/src/main/java/androidx/ui/node/ViewInterop.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/node/ViewInterop.kt
@@ -32,6 +32,7 @@
 import androidx.ui.unit.IntPx
 import androidx.ui.unit.ipx
 import androidx.ui.unit.isFinite
+import androidx.ui.util.fastFirstOrNull
 import androidx.ui.viewinterop.AndroidViewHolder
 
 /**
@@ -157,7 +158,7 @@
 
     inline fun <T : ViewAdapter> get(id: Int, factory: () -> T): T {
         @Suppress("UNCHECKED_CAST")
-        val existing = adapters.firstOrNull { it.id == id } as? T
+        val existing = adapters.fastFirstOrNull { it.id == id } as? T
         if (existing != null) return existing
         val next = factory()
         adapters.add(next)
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt b/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt
index 1b94655..375390a 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt
@@ -67,6 +67,7 @@
 import androidx.ui.unit.max
 import androidx.ui.unit.sp
 import androidx.ui.unit.toPx
+import androidx.ui.util.fastFirstOrNull
 
 /**
  * A TabRow contains a row of [Tab]s, and displays an indicator underneath the currently
@@ -285,13 +286,13 @@
 
                 // The divider is measured with its own height, and width equal to the total width
                 // of the tab row, and then placed on top of the tabs.
-                measurables.firstOrNull { it.tag == dividerTag }
+                measurables.fastFirstOrNull { it.tag == dividerTag }
                     ?.measure(constraints.copy(minWidth = layoutWidth, maxWidth = layoutWidth))
                     ?.run { place(IntPx.Zero, layoutHeight - height) }
 
                 // The indicator container is measured to fill the entire space occupied by the tab
                 // row, and then placed on top of the divider.
-                measurables.firstOrNull { it.tag == indicatorTag }
+                measurables.fastFirstOrNull { it.tag == indicatorTag }
                     ?.measure(Constraints.fixed(layoutWidth, layoutHeight))
                     ?.place(IntPx.Zero, IntPx.Zero)
             }
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 3b2f6c0..6758f33 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
@@ -30,6 +30,7 @@
 import androidx.ui.foundation.animation.fling
 import androidx.ui.foundation.gestures.DragDirection
 import androidx.ui.foundation.gestures.draggable
+import androidx.ui.util.fastFirstOrNull
 
 /**
  * Higher-level component that allows dragging around anchored positions binded to different states
@@ -74,7 +75,7 @@
     val forceAnimationCheck = state { true }
 
     val anchors = remember(anchorsToState) { anchorsToState.map { it.first } }
-    val currentValue = anchorsToState.firstOrNull { it.second == state }!!.first
+    val currentValue = anchorsToState.fastFirstOrNull { it.second == state }!!.first
     val flingConfig =
         AnchorsFlingConfig(anchors, animationBuilder, onAnimationEnd = { reason, finalValue, _ ->
             if (reason != AnimationEndReason.Interrupted) {
diff --git a/ui/ui-text-core/api/0.1.0-dev12.txt b/ui/ui-text-core/api/0.1.0-dev12.txt
index 946c57b..d08cf00 100644
--- a/ui/ui-text-core/api/0.1.0-dev12.txt
+++ b/ui/ui-text-core/api/0.1.0-dev12.txt
@@ -413,7 +413,7 @@
   }
 
   public final class ParagraphIntrinsicsKt {
-    method public static androidx.ui.text.ParagraphIntrinsics ParagraphIntrinsics(String text, androidx.ui.text.TextStyle style, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.SpanStyle>> spanStyles = listOf(), java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.Placeholder>> placeholders = listOf(), androidx.ui.unit.Density density, androidx.ui.text.font.Font.ResourceLoader resourceLoader);
+    method public static androidx.ui.text.ParagraphIntrinsics ParagraphIntrinsics(String text, androidx.ui.text.TextStyle style, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.SpanStyle>> spanStyles = emptyList(), java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.Placeholder>> placeholders = emptyList(), androidx.ui.unit.Density density, androidx.ui.text.font.Font.ResourceLoader resourceLoader);
   }
 
   public final class ParagraphKt {
diff --git a/ui/ui-text-core/api/current.txt b/ui/ui-text-core/api/current.txt
index 946c57b..d08cf00 100644
--- a/ui/ui-text-core/api/current.txt
+++ b/ui/ui-text-core/api/current.txt
@@ -413,7 +413,7 @@
   }
 
   public final class ParagraphIntrinsicsKt {
-    method public static androidx.ui.text.ParagraphIntrinsics ParagraphIntrinsics(String text, androidx.ui.text.TextStyle style, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.SpanStyle>> spanStyles = listOf(), java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.Placeholder>> placeholders = listOf(), androidx.ui.unit.Density density, androidx.ui.text.font.Font.ResourceLoader resourceLoader);
+    method public static androidx.ui.text.ParagraphIntrinsics ParagraphIntrinsics(String text, androidx.ui.text.TextStyle style, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.SpanStyle>> spanStyles = emptyList(), java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.Placeholder>> placeholders = emptyList(), androidx.ui.unit.Density density, androidx.ui.text.font.Font.ResourceLoader resourceLoader);
   }
 
   public final class ParagraphKt {
diff --git a/ui/ui-text-core/api/public_plus_experimental_0.1.0-dev12.txt b/ui/ui-text-core/api/public_plus_experimental_0.1.0-dev12.txt
index 946c57b..d08cf00 100644
--- a/ui/ui-text-core/api/public_plus_experimental_0.1.0-dev12.txt
+++ b/ui/ui-text-core/api/public_plus_experimental_0.1.0-dev12.txt
@@ -413,7 +413,7 @@
   }
 
   public final class ParagraphIntrinsicsKt {
-    method public static androidx.ui.text.ParagraphIntrinsics ParagraphIntrinsics(String text, androidx.ui.text.TextStyle style, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.SpanStyle>> spanStyles = listOf(), java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.Placeholder>> placeholders = listOf(), androidx.ui.unit.Density density, androidx.ui.text.font.Font.ResourceLoader resourceLoader);
+    method public static androidx.ui.text.ParagraphIntrinsics ParagraphIntrinsics(String text, androidx.ui.text.TextStyle style, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.SpanStyle>> spanStyles = emptyList(), java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.Placeholder>> placeholders = emptyList(), androidx.ui.unit.Density density, androidx.ui.text.font.Font.ResourceLoader resourceLoader);
   }
 
   public final class ParagraphKt {
diff --git a/ui/ui-text-core/api/public_plus_experimental_current.txt b/ui/ui-text-core/api/public_plus_experimental_current.txt
index 946c57b..d08cf00 100644
--- a/ui/ui-text-core/api/public_plus_experimental_current.txt
+++ b/ui/ui-text-core/api/public_plus_experimental_current.txt
@@ -413,7 +413,7 @@
   }
 
   public final class ParagraphIntrinsicsKt {
-    method public static androidx.ui.text.ParagraphIntrinsics ParagraphIntrinsics(String text, androidx.ui.text.TextStyle style, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.SpanStyle>> spanStyles = listOf(), java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.Placeholder>> placeholders = listOf(), androidx.ui.unit.Density density, androidx.ui.text.font.Font.ResourceLoader resourceLoader);
+    method public static androidx.ui.text.ParagraphIntrinsics ParagraphIntrinsics(String text, androidx.ui.text.TextStyle style, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.SpanStyle>> spanStyles = emptyList(), java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.Placeholder>> placeholders = emptyList(), androidx.ui.unit.Density density, androidx.ui.text.font.Font.ResourceLoader resourceLoader);
   }
 
   public final class ParagraphKt {
diff --git a/ui/ui-text-core/api/restricted_0.1.0-dev12.txt b/ui/ui-text-core/api/restricted_0.1.0-dev12.txt
index 07f2f76..6f7ebdd 100644
--- a/ui/ui-text-core/api/restricted_0.1.0-dev12.txt
+++ b/ui/ui-text-core/api/restricted_0.1.0-dev12.txt
@@ -418,7 +418,7 @@
   }
 
   public final class ParagraphIntrinsicsKt {
-    method public static androidx.ui.text.ParagraphIntrinsics ParagraphIntrinsics(String text, androidx.ui.text.TextStyle style, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.SpanStyle>> spanStyles = listOf(), java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.Placeholder>> placeholders = listOf(), androidx.ui.unit.Density density, androidx.ui.text.font.Font.ResourceLoader resourceLoader);
+    method public static androidx.ui.text.ParagraphIntrinsics ParagraphIntrinsics(String text, androidx.ui.text.TextStyle style, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.SpanStyle>> spanStyles = emptyList(), java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.Placeholder>> placeholders = emptyList(), androidx.ui.unit.Density density, androidx.ui.text.font.Font.ResourceLoader resourceLoader);
   }
 
   public final class ParagraphKt {
diff --git a/ui/ui-text-core/api/restricted_current.txt b/ui/ui-text-core/api/restricted_current.txt
index 07f2f76..6f7ebdd 100644
--- a/ui/ui-text-core/api/restricted_current.txt
+++ b/ui/ui-text-core/api/restricted_current.txt
@@ -418,7 +418,7 @@
   }
 
   public final class ParagraphIntrinsicsKt {
-    method public static androidx.ui.text.ParagraphIntrinsics ParagraphIntrinsics(String text, androidx.ui.text.TextStyle style, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.SpanStyle>> spanStyles = listOf(), java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.Placeholder>> placeholders = listOf(), androidx.ui.unit.Density density, androidx.ui.text.font.Font.ResourceLoader resourceLoader);
+    method public static androidx.ui.text.ParagraphIntrinsics ParagraphIntrinsics(String text, androidx.ui.text.TextStyle style, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.SpanStyle>> spanStyles = emptyList(), java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.Placeholder>> placeholders = emptyList(), androidx.ui.unit.Density density, androidx.ui.text.font.Font.ResourceLoader resourceLoader);
   }
 
   public final class ParagraphKt {
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/ParagraphIntrinsics.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/ParagraphIntrinsics.kt
index 6e4abe5..fe529f3 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/ParagraphIntrinsics.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/ParagraphIntrinsics.kt
@@ -45,8 +45,8 @@
 /* actual */ fun ParagraphIntrinsics(
     text: String,
     style: TextStyle,
-    spanStyles: List<AnnotatedString.Item<SpanStyle>> = listOf(),
-    placeholders: List<AnnotatedString.Item<Placeholder>> = listOf(),
+    spanStyles: List<AnnotatedString.Item<SpanStyle>> = emptyList(),
+    placeholders: List<AnnotatedString.Item<Placeholder>> = emptyList(),
     density: Density,
     resourceLoader: Font.ResourceLoader
 ): ParagraphIntrinsics {
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/platform/AndroidParagraphHelper.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/platform/AndroidParagraphHelper.kt
index 84fc451..2af715f 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/platform/AndroidParagraphHelper.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/platform/AndroidParagraphHelper.kt
@@ -65,6 +65,7 @@
 import androidx.ui.text.style.TextDecoration
 import androidx.ui.text.style.TextDirectionAlgorithm
 import androidx.ui.text.style.TextIndent
+import androidx.ui.util.fastForEach
 import kotlin.math.ceil
 import kotlin.math.roundToInt
 import android.os.LocaleList as AndroidLocaleList
@@ -426,7 +427,8 @@
         }
     }
 
-    for ((placeholder, start, end) in placeholders) {
+    placeholders.fastForEach {
+        val (placeholder, start, end) = it
         with(placeholder) {
             spannableString.setSpanWithPriority(
                 PlaceholderSpan(
@@ -531,4 +533,4 @@
         PlaceholderVerticalAlign.TextTop -> PlaceholderSpan.ALIGN_TEXT_TOP
         PlaceholderVerticalAlign.TextBottom -> PlaceholderSpan.ALIGN_TEXT_BOTTOM
         PlaceholderVerticalAlign.TextCenter -> PlaceholderSpan.ALIGN_TEXT_CENTER
-    }
\ No newline at end of file
+    }
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/TextUnit.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/TextUnit.kt
index 009e06f..d90d823 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/TextUnit.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/TextUnit.kt
@@ -172,6 +172,8 @@
     }
 
     companion object {
+        internal val TextUnitTypes = arrayOf(TextUnitType.Inherit, TextUnitType.Sp, TextUnitType.Em)
+
         /**
          * Creates a SP unit [TextUnit].
          */
@@ -221,12 +223,7 @@
      *
      * @throws RuntimeException if unknown unknown unit type is appeared.
      */
-    val type: TextUnitType get() = when (rawType) {
-        UNIT_TYPE_INHERIT -> TextUnitType.Inherit
-        UNIT_TYPE_SP -> TextUnitType.Sp
-        UNIT_TYPE_EM -> TextUnitType.Em
-        else -> throw RuntimeException("packed TextUnit has unknown unit type.")
-    }
+    val type: TextUnitType get() = TextUnitTypes[(rawType ushr 32).toInt()]
 
     /**
      * True if this is [TextUnit.Inherit], otherwise false.
diff --git a/ui/ui-util/api/0.1.0-dev12.txt b/ui/ui-util/api/0.1.0-dev12.txt
index b30e285..9286e03 100644
--- a/ui/ui-util/api/0.1.0-dev12.txt
+++ b/ui/ui-util/api/0.1.0-dev12.txt
@@ -30,6 +30,7 @@
 
   public final class ListUtilsKt {
     method public static inline <T> boolean fastAny(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+    method public static inline <T> T? fastFirstOrNull(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
     method public static inline <T> void fastForEach(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action);
   }
 
diff --git a/ui/ui-util/api/current.txt b/ui/ui-util/api/current.txt
index b30e285..9286e03 100644
--- a/ui/ui-util/api/current.txt
+++ b/ui/ui-util/api/current.txt
@@ -30,6 +30,7 @@
 
   public final class ListUtilsKt {
     method public static inline <T> boolean fastAny(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+    method public static inline <T> T? fastFirstOrNull(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
     method public static inline <T> void fastForEach(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action);
   }
 
diff --git a/ui/ui-util/api/public_plus_experimental_0.1.0-dev12.txt b/ui/ui-util/api/public_plus_experimental_0.1.0-dev12.txt
index b30e285..9286e03 100644
--- a/ui/ui-util/api/public_plus_experimental_0.1.0-dev12.txt
+++ b/ui/ui-util/api/public_plus_experimental_0.1.0-dev12.txt
@@ -30,6 +30,7 @@
 
   public final class ListUtilsKt {
     method public static inline <T> boolean fastAny(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+    method public static inline <T> T? fastFirstOrNull(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
     method public static inline <T> void fastForEach(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action);
   }
 
diff --git a/ui/ui-util/api/public_plus_experimental_current.txt b/ui/ui-util/api/public_plus_experimental_current.txt
index b30e285..9286e03 100644
--- a/ui/ui-util/api/public_plus_experimental_current.txt
+++ b/ui/ui-util/api/public_plus_experimental_current.txt
@@ -30,6 +30,7 @@
 
   public final class ListUtilsKt {
     method public static inline <T> boolean fastAny(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+    method public static inline <T> T? fastFirstOrNull(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
     method public static inline <T> void fastForEach(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action);
   }
 
diff --git a/ui/ui-util/api/restricted_0.1.0-dev12.txt b/ui/ui-util/api/restricted_0.1.0-dev12.txt
index b30e285..9286e03 100644
--- a/ui/ui-util/api/restricted_0.1.0-dev12.txt
+++ b/ui/ui-util/api/restricted_0.1.0-dev12.txt
@@ -30,6 +30,7 @@
 
   public final class ListUtilsKt {
     method public static inline <T> boolean fastAny(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+    method public static inline <T> T? fastFirstOrNull(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
     method public static inline <T> void fastForEach(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action);
   }
 
diff --git a/ui/ui-util/api/restricted_current.txt b/ui/ui-util/api/restricted_current.txt
index b30e285..9286e03 100644
--- a/ui/ui-util/api/restricted_current.txt
+++ b/ui/ui-util/api/restricted_current.txt
@@ -30,6 +30,7 @@
 
   public final class ListUtilsKt {
     method public static inline <T> boolean fastAny(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+    method public static inline <T> T? fastFirstOrNull(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
     method public static inline <T> void fastForEach(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action);
   }
 
diff --git a/ui/ui-util/src/commonMain/kotlin/androidx/ui/util/ListUtils.kt b/ui/ui-util/src/commonMain/kotlin/androidx/ui/util/ListUtils.kt
index f032a4f..cc900d5 100644
--- a/ui/ui-util/src/commonMain/kotlin/androidx/ui/util/ListUtils.kt
+++ b/ui/ui-util/src/commonMain/kotlin/androidx/ui/util/ListUtils.kt
@@ -34,3 +34,11 @@
     fastForEach { if (predicate(it)) return true }
     return false
 }
+
+/**
+ * Returns the first value that [predicate] returns `true` for or `null` if nothing matches.
+ */
+inline fun <T> List<T>.fastFirstOrNull(predicate: (T) -> Boolean): T? {
+    fastForEach { if (predicate(it)) return it }
+    return null
+}
\ No newline at end of file
diff --git a/ui/ui-util/src/unitTest/kotlin/androidx/ui/util/ListUtilsTest.kt b/ui/ui-util/src/unitTest/kotlin/androidx/ui/util/ListUtilsTest.kt
index 8d1ae60..46ae408 100644
--- a/ui/ui-util/src/unitTest/kotlin/androidx/ui/util/ListUtilsTest.kt
+++ b/ui/ui-util/src/unitTest/kotlin/androidx/ui/util/ListUtilsTest.kt
@@ -18,6 +18,7 @@
 
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
 import org.junit.Assert.assertTrue
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -69,4 +70,15 @@
         val list = listOf(0, -1, -500, 1)
         assertTrue(list.fastAny { it > 0 })
     }
+
+    @Test
+    fun firstOrNullNotFound() {
+        val list = listOf(0, -1, -500)
+        assertNull(list.fastFirstOrNull { it > 0 })
+    }
+    @Test
+    fun firstOrNullFound() {
+        val list = listOf(0, -1, -500, 1)
+        assertEquals(1, list.fastFirstOrNull { it > 0 })
+    }
 }
\ No newline at end of file