diff --git a/Runtime/engine/UIWidgetsPanel.cs b/Runtime/engine/UIWidgetsPanel.cs
index a72ffabf..c886fea3 100644
--- a/Runtime/engine/UIWidgetsPanel.cs
+++ b/Runtime/engine/UIWidgetsPanel.cs
@@ -138,6 +138,13 @@ void _handleViewMetricsChanged(string method, List<JSONNode> args) {
             this._displayMetrics.onViewMetricsChanged();
         }
 
+        protected virtual void InitWindowAdapter() {
+            D.assert(this._windowAdapter == null);
+            this._windowAdapter = new UIWidgetWindowAdapter(this);
+
+            this._windowAdapter.OnEnable();
+        }
+
         protected override void OnEnable() {
             base.OnEnable();
 
@@ -153,10 +160,7 @@ protected override void OnEnable() {
                 _repaintEvent = new Event {type = EventType.Repaint};
             }
 
-            D.assert(this._windowAdapter == null);
-            this._windowAdapter = new UIWidgetWindowAdapter(this);
-
-            this._windowAdapter.OnEnable();
+            this.InitWindowAdapter();
 
             Widget root;
             using (this._windowAdapter.getScope()) {
diff --git a/Runtime/engine/raycastable.meta b/Runtime/engine/raycastable.meta
new file mode 100644
index 00000000..4b305bd7
--- /dev/null
+++ b/Runtime/engine/raycastable.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d8e15e1221dae40e68226ec6d8ed8ddb
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/engine/raycastable/RaycastManager.cs b/Runtime/engine/raycastable/RaycastManager.cs
new file mode 100644
index 00000000..f9c7aca8
--- /dev/null
+++ b/Runtime/engine/raycastable/RaycastManager.cs
@@ -0,0 +1,125 @@
+using System.Collections.Generic;
+using Unity.UIWidgets.foundation;
+using Unity.UIWidgets.ui;
+using UnityEngine;
+
+namespace Unity.UIWidgets.engine.raycast {
+    public class RaycastableRect {
+        bool _isDirty = true;
+
+        public bool isDirty {
+            get { return this._isDirty; }
+        }
+
+        public float left;
+        public float right;
+        public float top;
+        public float bottom;
+
+        public void MarkDirty() {
+            this._isDirty = true;
+        }
+
+        public void UnmarkDirty() {
+            this._isDirty = false;
+        }
+
+        public void UpdateRect(float left, float top, float width, float height) {
+            this.left = left;
+            this.right = left + width;
+            this.top = top;
+            this.bottom = top + height;
+        }
+
+        public bool CheckInRect(Vector2 pos) {
+            return pos.x >= this.left &&
+                   pos.x < this.right &&
+                   pos.y >= this.top &&
+                   pos.y < this.bottom;
+        }
+    }
+
+    public class RaycastManager {
+        static RaycastManager _instance;
+
+        public static RaycastManager instance {
+            get {
+                if (_instance == null) {
+                    _instance = new RaycastManager();
+                }
+
+                return _instance;
+            }
+        }
+
+        public readonly Dictionary<int, Dictionary<int, RaycastableRect>> raycastHandlerMap =
+            new Dictionary<int, Dictionary<int, RaycastableRect>>();
+
+        public static void NewWindow(int windowHashCode) {
+            if (!instance.raycastHandlerMap.ContainsKey(windowHashCode)) {
+                instance.raycastHandlerMap.Add(windowHashCode, new Dictionary<int, RaycastableRect>());
+            }
+        }
+
+        public static void DisposeWindow(int windowHashCode) {
+            if (instance.raycastHandlerMap.ContainsKey(windowHashCode)) {
+                instance.raycastHandlerMap.Remove(windowHashCode);
+            }
+        }
+
+        public static void AddToList(int widgetHashCode, int windowHashCode) {
+            D.assert(instance.raycastHandlerMap.ContainsKey(windowHashCode), () =>
+                $"Raycast Handler Map doesn't contain Window {windowHashCode}, " +
+                $"Make sure using UIWidgetsRaycastablePanel instead of UIWidgetsPanel " +
+                $"while using RaycastableContainer.");
+            D.assert(!instance.raycastHandlerMap[windowHashCode].ContainsKey(widgetHashCode), () =>
+                $"Raycast Handler Map already contains Widget {widgetHashCode} at Window {windowHashCode}");
+
+            instance.raycastHandlerMap[windowHashCode][widgetHashCode] = new RaycastableRect();
+        }
+
+        public static void MarkDirty(int widgetHashCode, int windowHashCode) {
+            D.assert(instance.raycastHandlerMap.ContainsKey(windowHashCode), () =>
+                $"Raycast Handler Map doesn't contain Window {windowHashCode}");
+            D.assert(instance.raycastHandlerMap[windowHashCode].ContainsKey(widgetHashCode), () =>
+                $"Raycast Handler Map doesn't contain Widget {widgetHashCode} at Window {windowHashCode}");
+
+            instance.raycastHandlerMap[windowHashCode][widgetHashCode].MarkDirty();
+        }
+
+        public static void UpdateSizeOffset(int widgetHashCode, int windowHashCode, Size size, Offset offset) {
+            D.assert(instance.raycastHandlerMap.ContainsKey(windowHashCode), () =>
+                $"Raycast Handler Map doesn't contain Window {windowHashCode}");
+            D.assert(instance.raycastHandlerMap[windowHashCode].ContainsKey(widgetHashCode), () =>
+                $"Raycast Handler Map doesn't contain Widget {widgetHashCode} at Window {windowHashCode}");
+
+            if (instance.raycastHandlerMap[windowHashCode][widgetHashCode].isDirty) {
+                instance.raycastHandlerMap[windowHashCode][widgetHashCode]
+                    .UpdateRect(offset.dx, offset.dy, size.width, size.height);
+                instance.raycastHandlerMap[windowHashCode][widgetHashCode].UnmarkDirty();
+            }
+        }
+
+        public static void RemoveFromList(int widgetHashCode, int windowHashCode) {
+            D.assert(instance.raycastHandlerMap.ContainsKey(windowHashCode), () =>
+                $"Raycast Handler Map doesn't contain Window {windowHashCode}");
+            D.assert(instance.raycastHandlerMap[windowHashCode].ContainsKey(widgetHashCode), () =>
+                $"Raycast Handler Map doesn't contain Widget {widgetHashCode} at Window {windowHashCode}");
+
+            instance.raycastHandlerMap[windowHashCode].Remove(widgetHashCode);
+        }
+
+        public static bool CheckCastThrough(int windowHashCode, Vector2 pos) {
+            D.assert(instance.raycastHandlerMap.ContainsKey(windowHashCode), () =>
+                $"Raycast Handler Map doesn't contain Window {windowHashCode}");
+
+            foreach (var item in instance.raycastHandlerMap[windowHashCode]) {
+                if (item.Value.CheckInRect(pos)) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Runtime/engine/raycastable/RaycastManager.cs.meta b/Runtime/engine/raycastable/RaycastManager.cs.meta
new file mode 100644
index 00000000..74519d43
--- /dev/null
+++ b/Runtime/engine/raycastable/RaycastManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9574f12b230354e6f87fc5fc0c98c96e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/engine/raycastable/RaycastableContainer.cs b/Runtime/engine/raycastable/RaycastableContainer.cs
new file mode 100644
index 00000000..429d6524
--- /dev/null
+++ b/Runtime/engine/raycastable/RaycastableContainer.cs
@@ -0,0 +1,106 @@
+using Unity.UIWidgets.foundation;
+using Unity.UIWidgets.rendering;
+using Unity.UIWidgets.ui;
+using Unity.UIWidgets.widgets;
+
+namespace Unity.UIWidgets.engine.raycast {
+    class RaycastableBox : SingleChildRenderObjectWidget {
+        public RaycastableBox(
+            Key key = null,
+            Widget child = null
+        ) : base(key, child) {
+            this.windowHashCode = Window.instance.GetHashCode();
+        }
+
+        readonly int windowHashCode;
+
+        public override RenderObject createRenderObject(BuildContext context) {
+            return new RenderRaycastableBox(
+                windowHashCode: this.windowHashCode,
+                widget: this
+            );
+        }
+
+        public override Element createElement() {
+            return new _RaycastableBoxRenderElement(windowHashCode: this.windowHashCode, widget: this);
+        }
+    }
+
+    class RenderRaycastableBox : RenderProxyBox {
+        public RenderRaycastableBox(
+            int windowHashCode,
+            RaycastableBox widget
+        ) {
+            this.widgetHashCode = widget.GetHashCode();
+            this.windowHashCode = windowHashCode;
+        }
+
+        readonly int widgetHashCode;
+        readonly int windowHashCode;
+
+        public override void paint(PaintingContext context, Offset offset) {
+            RaycastManager.UpdateSizeOffset(this.widgetHashCode, this.windowHashCode, this.size, offset);
+
+            base.paint(context, offset);
+        }
+    }
+
+    class _RaycastableBoxRenderElement : SingleChildRenderObjectElement {
+        public _RaycastableBoxRenderElement(
+            int windowHashCode,
+            RaycastableBox widget
+        ) : base(widget) {
+            this.windowHashCode = windowHashCode;
+        }
+
+        public new RaycastableBox widget {
+            get { return base.widget as RaycastableBox; }
+        }
+
+        int widgetHashCode;
+        int windowHashCode;
+
+        public override void mount(Element parent, object newSlot) {
+            this.widgetHashCode = this.widget.GetHashCode();
+            RaycastManager.AddToList(this.widgetHashCode, this.windowHashCode);
+            base.mount(parent, newSlot);
+        }
+
+        public override void update(Widget newWidget) {
+            RaycastManager.MarkDirty(this.widgetHashCode, this.windowHashCode);
+            base.update(newWidget);
+        }
+
+        public override void unmount() {
+            RaycastManager.RemoveFromList(this.widgetHashCode, this.windowHashCode);
+            base.unmount();
+        }
+    }
+
+    public class RaycastableContainer : StatelessWidget {
+        public RaycastableContainer(
+            Widget child = null,
+            Key key = null
+        ) : base(key) {
+            this.child = child;
+        }
+
+        public readonly Widget child;
+
+        public override Widget build(BuildContext context) {
+            Widget current = this.child;
+
+            if (this.child == null) {
+                current = new LimitedBox(
+                    maxWidth: 0.0f,
+                    maxHeight: 0.0f,
+                    child: new ConstrainedBox(constraints: BoxConstraints.expand())
+                );
+            }
+
+            current = new RaycastableBox(child: current);
+
+            return current;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Runtime/engine/raycastable/RaycastableContainer.cs.meta b/Runtime/engine/raycastable/RaycastableContainer.cs.meta
new file mode 100644
index 00000000..3bb75ef0
--- /dev/null
+++ b/Runtime/engine/raycastable/RaycastableContainer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9af0c7d6aab134f5ba187ff34acf2377
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs b/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs
new file mode 100644
index 00000000..2fc70de1
--- /dev/null
+++ b/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs
@@ -0,0 +1,40 @@
+using Unity.UIWidgets.engine;
+using UnityEngine;
+
+namespace Unity.UIWidgets.engine.raycast {
+    [RequireComponent(typeof(RectTransform))]
+    public class UIWidgetsRaycastablePanel : UIWidgetsPanel, ICanvasRaycastFilter {
+        int windowHashCode;
+
+        protected override void InitWindowAdapter() {
+            base.InitWindowAdapter();
+            this.windowHashCode = this.window.GetHashCode();
+            RaycastManager.NewWindow(this.windowHashCode);
+        }
+
+        protected override void OnDisable() {
+            base.OnDisable();
+            RaycastManager.DisposeWindow(this.windowHashCode);
+        }
+
+        public bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera) {
+            if (!this.enabled) {
+                return true;
+            }
+
+            Vector2 local;
+            RectTransformUtility.ScreenPointToLocalPointInRectangle(this.rectTransform, screenPoint, eventCamera,
+                out local);
+
+            Rect rect = this.rectTransform.rect;
+
+            // Convert top left corner as reference origin point.
+            local.x += this.rectTransform.pivot.x * rect.width;
+            local.y -= this.rectTransform.pivot.y * rect.height;
+            local.x = local.x / this.devicePixelRatio;
+            local.y = -local.y / this.devicePixelRatio;
+
+            return !RaycastManager.CheckCastThrough(this.windowHashCode, local);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs.meta b/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs.meta
new file mode 100644
index 00000000..f5ff1f78
--- /dev/null
+++ b/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e5265d12f7193408b90993bdf987f058
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: