浏览代码

Merge branches 'kgdev' and 'master' of github.com:UnityTech/UIWidgets into kgdev

/main
kg 6 年前
当前提交
ae4a0018
共有 76 个文件被更改,包括 8165 次插入248 次删除
  1. 6
      .DS_Store
  2. 4
      README-ZH.md
  3. 21
      README.md
  4. 26
      Runtime/editor/editor_utils.cs
  5. 29
      Runtime/editor/editor_window.cs
  6. 2
      Runtime/editor/rasterizer.cs
  7. 93
      Runtime/engine/DisplayMetrics.cs
  8. 41
      Runtime/engine/UIWidgetsPanel.cs
  9. 29
      Runtime/flow/compositor_context.cs
  10. 2
      Runtime/flow/layer.cs
  11. 4
      Runtime/flow/layer_tree.cs
  12. 55
      Runtime/flow/performance_overlay_layer.cs
  13. 3
      Runtime/material/input_decorator.cs
  14. 2
      Runtime/material/toggleable.cs
  15. 3
      Runtime/rendering/binding.cs
  16. 35
      Runtime/rendering/flex.cs
  17. 7
      Runtime/rendering/shifted_box.cs
  18. 17
      Runtime/ui/window.cs
  19. 15
      Runtime/widgets/app.cs
  20. 43
      Runtime/widgets/basic.cs
  21. 2
      Runtime/widgets/safe_area.cs
  22. 81
      Runtime/widgets/sliver.cs
  23. 70
      Runtime/widgets/sliver_persistent_header.cs
  24. 2
      Runtime/widgets/text.cs
  25. 4
      Samples/UIWidgetsGallery/gallery/demo.cs
  26. 18
      Samples/UIWidgetsGallery/gallery/demos.cs
  27. 2
      Runtime/flow/instrumentation.cs.meta
  28. 34
      Runtime/Plugins/platform/ios/UIWidgetsViewController.h
  29. 26
      Runtime/Plugins/platform/ios/UIWidgetsViewController.h.meta
  30. 108
      Runtime/Plugins/platform/ios/UIWidgetsViewController.mm
  31. 36
      Runtime/Plugins/platform/ios/UIWidgetsViewController.mm.meta
  32. 113
      Runtime/flow/instrumentation.cs
  33. 310
      Runtime/material/checkbox.cs
  34. 3
      Runtime/material/checkbox.cs.meta
  35. 275
      Runtime/rendering/debug_overflow_indicator.cs
  36. 11
      Runtime/rendering/debug_overflow_indicator.cs.meta
  37. 451
      Runtime/rendering/sliver_grid.cs
  38. 11
      Runtime/rendering/sliver_grid.cs.meta
  39. 553
      Runtime/widgets/heroes.cs
  40. 3
      Runtime/widgets/heroes.cs.meta
  41. 63
      Samples/UIWidgetSample/HttpRequestSample.cs
  42. 11
      Samples/UIWidgetSample/HttpRequestSample.cs.meta
  43. 8
      Samples/UIWidgetsGallery/demo/shrine.meta
  44. 40
      Samples/UIWidgetsGallery/demo/shrine_demo.cs
  45. 3
      Samples/UIWidgetsGallery/demo/shrine_demo.cs.meta
  46. 8
      Tests/Resources/people.meta
  47. 280
      Samples/UIWidgetsGallery/demo/shrine/shrine_data.cs
  48. 3
      Samples/UIWidgetsGallery/demo/shrine/shrine_data.cs.meta
  49. 410
      Samples/UIWidgetsGallery/demo/shrine/shrine_home.cs
  50. 11
      Samples/UIWidgetsGallery/demo/shrine/shrine_home.cs.meta
  51. 3
      Samples/UIWidgetsGallery/demo/shrine/shrine_order.cs.meta
  52. 156
      Samples/UIWidgetsGallery/demo/shrine/shrine_page.cs
  53. 3
      Samples/UIWidgetsGallery/demo/shrine/shrine_page.cs.meta
  54. 98
      Samples/UIWidgetsGallery/demo/shrine/shrine_theme.cs
  55. 3
      Samples/UIWidgetsGallery/demo/shrine/shrine_theme.cs.meta
  56. 153
      Samples/UIWidgetsGallery/demo/shrine/shrine_types.cs
  57. 3
      Samples/UIWidgetsGallery/demo/shrine/shrine_types.cs.meta
  58. 341
      Samples/UIWidgetsGallery/demo/shrine/shrine_order.cs
  59. 1001
      Tests/Resources/people/ali_landscape.png
  60. 88
      Tests/Resources/people/ali_landscape.png.meta
  61. 8
      Tests/Resources/people/square.meta
  62. 457
      Tests/Resources/people/square/ali.png
  63. 88
      Tests/Resources/people/square/ali.png.meta
  64. 509
      Tests/Resources/people/square/peter.png
  65. 88
      Tests/Resources/people/square/peter.png.meta
  66. 698
      Tests/Resources/people/square/sandra.png
  67. 88
      Tests/Resources/people/square/sandra.png.meta
  68. 393
      Tests/Resources/people/square/stella.png
  69. 88
      Tests/Resources/people/square/stella.png.meta
  70. 574
      Tests/Resources/people/square/trevor.png
  71. 88
      Tests/Resources/people/square/trevor.png.meta
  72. 98
      Runtime/service/performance_utils.cs
  73. 0
      /Runtime/flow/instrumentation.cs.meta

6
.DS_Store


Bud1  d.batIl  @� @� @� @ build.batIlocblob;(������build.bat.metaIlocblob�(������build.shIlocblob(������ build.sh.metaIlocblob�(������ CHANGELOG.mdIlocblob�(������CHANGELOG.md.metaIlocblob;�������CONTRIBUTING.mdIlocblob��������CONTRIBUTING.md.metaIlocblob�������Documentation~Ilocblob��������EditorIlocblob�������� Editor.metaIlocblob;������
Bud1 GELOG.  @� @� @� @ CHANGELOG.mdIlocblob�(������CHANGELOG.md.metaIlocblob;�������CONTRIBUTING.mdIlocblob��������CONTRIBUTING.md.metaIlocblob�������Documentation~Ilocblob��������EditorIlocblob�������� Editor.metaIlocblob;������
Tests.metaIlocblob�X������Third Party Notices.mdIlocblobX������Third Party Notices.md.metaIlocblob�X������!UIWidgetCleanupPlugin.DotSettingsIlocblob�X������&UIWidgetCleanupPlugin.DotSettings.metaIlocblob;������� E DSDB `� @� @� @lesIlocblob�������� Samples.metaIlocblob�������scriptsIlocblob�������� scripts.metaIlocblob��������TestsIlocblob;X������
Tests.metaIlocblob�X������Third Party Notices.mdIlocblobX������Third Party Notices.md.metaIlocblob�X������!UIWidgetCleanupPlugin.DotSettingsIlocblob�X������&UIWidgetCleanupPlugin.DotSettings.metaIlocblob;�������
Tests.metaIlocblob�X������Third Party Notices.mdIlocblobX������Third Party Notices.md.metaIlocblob�X������!UIWidgetCleanupPlugin.DotSettingsIlocblob�X������&UIWidgetCleanupPlugin.DotSettings.metaIlocblob;������� E DSDB `� @� @� @;X������
Tests.metaIlocblob�X������Third Party Notices.mdIlocblobX������Third Party Notices.md.metaIlocblob�X������!UIWidgetCleanupPlugin.DotSettingsIlocblob�X������&UIWidgetCleanupPlugin.DotSettings.metaIlocblob;�������

4
README-ZH.md


#### 三、创建小部件
UIWidgets应用是用**C#脚本**来编写的。 请按照以下步骤创建应用程序并在Unity编辑器中播放。
1. 创建一个新C#脚本,命名为“ExampleCanvas.cs”,并将以下代码粘贴到其中。
1. 创建一个新C#脚本,命名为“UIWidgetsExample.cs”,并将以下代码粘贴到其中。
```none
using System.Collections.Generic;

#### Wiki
目前开发团队仍在改进UIWidgets Wiki。 由于UIWidgets主要来源于Flutter,你也可以参考Flutter Wiki中与UIWidgets API对应部分的详细描述。
目前开发团队仍在改进UIWidgets Wiki。 由于UIWidgets主要来源于Flutter,你也可以参考Flutter Wiki中与UIWidgets API对应部分的详细描述。同时,你可以加入我们的讨论组(https://connect.unity.com/g/uiwidgets)。
#### 常问问题解答

21
README.md


UIWidgets is a plugin package for Unity Editor which helps developers to create, debug and deploy efficient,
cross-platform Apps using the Unity Engine.
UIWidgets is mainly derived from Flutter @https://github.com/flutter/flutter. However, taking advantage of
UIWidgets is mainly derived from [Flutter](https://github.com/flutter/flutter). However, taking advantage of
the powerful Unity Engine, it offers developers many new features to improve their Apps
as well as the develop workflow significantly.

2. You can add loading1@2.gif.bytes and loading1@3.gif.bytes in the same folder to support HD screens.
3. Use Image.asset("loading1.gif") to load the gif images.
#### Using Window Scope
If you see the error ```AssertionError: Window.instance is null``` or null pointer error of ```Window.instance```,
it means the code is not running in the window scope. In this case, you can enclose your code
with window scope as below:
```
using(WindowProvider.of(your gameObject with UIWidgetsPanel).getScope()) {
// code dealing with UIWidgets,
// e.g. setState(() => {....})
}
```
This is needed if the code is in methods
not invoked by UIWidgets. For example, if the code is in ```completed``` callback of ```UnityWebRequest```,
you need to enclose them with window scope.
Please see [HttpRequestSample](./Samples/UIWidgetSample/HttpRequestSample.cs) for detail.
For callback/event handler methods from UIWidgets (e.g ```Widget.build, State.initState...```), you don't need do
it yourself, since the framework ensure it's in window scope.
## Debug UIWidgets Application
#### Define UIWidgets_DEBUG

The develop team is still working on the UIWidgets Wiki. However, since UIWidgets is mainly derived from Flutter,
you can refer to Flutter Wiki to access detailed descriptions of UIWidgets APIs
from those of their Flutter counterparts.
Meanwhile, you can join the discussion channel at (https://connect.unity.com/g/uiwidgets)
#### FAQ

26
Runtime/editor/editor_utils.cs


using System.Collections;
using System.Reflection;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.ui;
using UnityEngine;

public float devicePixelRatio {
get { return this._lastDevicePixelRatio; }
}
public viewMetrics viewMetrics {
get {
return new viewMetrics {
insets_bottom = 0,
insets_left = 0,
insets_right = 0,
insets_top = 0,
padding_left = 0,
padding_top = 0,
padding_right = 0,
padding_bottom = 0
};
}
}
public WindowPadding viewPadding {
get { return WindowPadding.zero; }
}
public WindowPadding viewInsets {
get { return WindowPadding.zero; }
}
}
static class GameViewUtil {

29
Runtime/editor/editor_window.cs


namespace Unity.UIWidgets.editor {
#if UNITY_EDITOR
public abstract class UIWidgetsEditorWindow : EditorWindow {
public abstract class UIWidgetsEditorWindow : EditorWindow, WindowHost {
WindowAdapter _windowAdapter;
public UIWidgetsEditorWindow() {

}
protected abstract Widget createWidget();
public Window window {
get { return this._windowAdapter; }
}
}
public class EditorWindowAdapter : WindowAdapter {

}
float? _lastUpdateTime;
protected override float getUnscaledDeltaTime() {
if (this._lastUpdateTime == null) {
this._lastUpdateTime = (float) EditorApplication.timeSinceStartup;

this._lastUpdateTime = (float) EditorApplication.timeSinceStartup;
return deltaTime;
}
}
public interface WindowHost {
Window window { get; }
}
public abstract class WindowAdapter : Window {
static readonly List<WindowAdapter> _windowAdapters = new List<WindowAdapter>();

float _lastWindowWidth;
float _lastWindowHeight;
bool _viewMetricsChanged;
readonly MicrotaskQueue _microtaskQueue = new MicrotaskQueue();
readonly TimerProvider _timerProvider = new TimerProvider();
readonly Rasterizer _rasterizer = new Rasterizer();

}
protected virtual void updateSafeArea() {
}
public void onViewMetricsChanged() {
this._viewMetricsChanged = true;
}
protected abstract bool hasFocus();

return true;
}
if (this._viewMetricsChanged) {
return true;
}
return false;
}

this._lastWindowHeight * this._devicePixelRatio);
this.updateSafeArea();
this._viewMetricsChanged = false;
if (this.onMetricsChanged != null) {
this.onMetricsChanged();
}

}
public void Update() {
PerformanceUtils.instance.updateDeltaTime(this.getUnscaledDeltaTime());
this.updateDeltaTime(this.getUnscaledDeltaTime());
Timer.update();
bool hasFocus = this.hasFocus();

2
Runtime/editor/rasterizer.cs


var canvas = frame.getCanvas();
using (var compositorFrame = this._compositorContext.acquireFrame(canvas)) {
using (var compositorFrame = this._compositorContext.acquireFrame(canvas, true)) {
if (compositorFrame != null && compositorFrame.raster(layerTree, false)) {
frame.submit();
this._fireNextFrameCallbackIfPresent();

93
Runtime/engine/DisplayMetrics.cs


using System;
using System.Runtime.InteropServices;
using Unity.UIWidgets.ui;
[StructLayout(LayoutKind.Sequential)]
public struct viewMetrics {
public float insets_top;
public float insets_bottom;
public float insets_left;
public float insets_right;
public float padding_top;
public float padding_bottom;
public float padding_left;
public float padding_right;
}
public static class DisplayMetricsProvider {
public static Func<DisplayMetrics> provider = () => new PlayerDisplayMetrics();
}

void Update();
float devicePixelRatio { get; }
viewMetrics viewMetrics { get; }
WindowPadding viewPadding { get; }
WindowPadding viewInsets { get; }
viewMetrics? _viewMetrics = null;
public void OnEnable() {
}

}
public void Update() {
//view metrics marks dirty
this._viewMetrics = null;
}

}
public WindowPadding viewPadding {
get {
return new WindowPadding(this.viewMetrics.padding_left,
this.viewMetrics.padding_top,
this.viewMetrics.padding_right,
this.viewMetrics.padding_bottom);
}
}
public WindowPadding viewInsets {
get {
return new WindowPadding(this.viewMetrics.insets_left,
this.viewMetrics.insets_top,
this.viewMetrics.insets_right,
this.viewMetrics.insets_bottom);
}
}
public viewMetrics viewMetrics {
get {
if (this._viewMetrics != null) {
return this._viewMetrics.Value;
}
#if UNITY_ANDROID
this._viewMetrics = new viewMetrics {
insets_bottom = 0,
insets_left = 0,
insets_right = 0,
insets_top = 0,
padding_left = Screen.safeArea.x,
padding_top = Screen.safeArea.y,
padding_right = Screen.width - Screen.safeArea.width - Screen.safeArea.x,
padding_bottom = Screen.height - Screen.safeArea.height - Screen.safeArea.y
};
#elif UNITY_WEBGL
this._viewMetrics = new viewMetrics {
insets_bottom = 0,
insets_left = 0,
insets_right = 0,
insets_top = 0,
padding_left = 0,
padding_top = 0,
padding_right = 0,
padding_bottom = 0
};
#elif UNITY_IOS
viewMetrics metrics = IOSGetViewportPadding();
this._viewMetrics = metrics;
#else
this._viewMetrics = new viewMetrics {
insets_bottom = 0,
insets_left = 0,
insets_right = 0,
insets_top = 0,
padding_left = 0,
padding_top = 0,
padding_right = 0,
padding_bottom = 0
};
#endif
return this._viewMetrics.Value;
}
}
#if UNITY_ANDROID
static float AndroidDevicePixelRatio() {
using (

#if UNITY_IOS
[DllImport("__Internal")]
static extern int IOSDeviceScaleFactor();
[DllImport("__Internal")]
static extern viewMetrics IOSGetViewportPadding();
}
}

41
Runtime/engine/UIWidgetsPanel.cs


using System.Collections.Generic;
using Unity.UIWidgets.async;
using Unity.UIWidgets.editor;
using Unity.UIWidgets.external.simplejson;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;

readonly UIWidgetsPanel _uiWidgetsPanel;
bool _needsPaint;
this._padding = new WindowPadding(
Screen.safeArea.x,
Screen.safeArea.y,
Screen.width - Screen.safeArea.width - Screen.safeArea.x,
Screen.height - Screen.safeArea.height - Screen.safeArea.y);
this._padding = this._uiWidgetsPanel.viewPadding;
this._viewInsets = this._uiWidgetsPanel.viewInsets;
}
protected override bool hasFocus() {

[RequireComponent(typeof(RectTransform))]
public class UIWidgetsPanel : RawImage, IPointerDownHandler, IPointerUpHandler, IDragHandler,
IPointerEnterHandler, IPointerExitHandler {
IPointerEnterHandler, IPointerExitHandler, WindowHost {
static Event _repaintEvent;
[SerializeField] protected float devicePixelRatioOverride;

HashSet<int> _enteredPointers;
bool _viewMetricsCallbackRegistered;
bool _mouseEntered {
get { return !this._enteredPointers.isEmpty(); }

const int mouseButtonNum = 3;
void _handleViewMetricsChanged(string method, List<JSONNode> args) {
this._windowAdapter.onViewMetricsChanged();
this._displayMetrics.Update();
}
protected override void OnEnable() {
base.OnEnable();
//Disable the default touch -> mouse event conversion on mobile devices

this._displayMetrics.OnEnable();
if (_repaintEvent == null) {
_repaintEvent = new Event {type = EventType.Repaint};
}

}
}
protected override void OnDisable() {
D.assert(this._windowAdapter != null);
this._windowAdapter.OnDisable();
this._windowAdapter = null;
base.OnDisable();
public WindowPadding viewPadding {
get { return this._displayMetrics.viewPadding; }
}
public WindowPadding viewInsets {
get { return this._displayMetrics.viewInsets; }
}
protected virtual Widget createWidget() {

protected virtual void Update() {
this._displayMetrics.Update();
UIWidgetsMessageManager.ensureUIWidgetsMessageManagerIfNeeded();
if (!this._viewMetricsCallbackRegistered) {
this._viewMetricsCallbackRegistered = true;
UIWidgetsMessageManager.instance?.AddChannelMessageDelegate("ViewportMatricsChanged",
this._handleViewMetricsChanged);
}
if (this._mouseEntered) {
if (this._lastMouseMove.x != Input.mousePosition.x || this._lastMouseMove.y != Input.mousePosition.y) {

physicalX: position.x,
physicalY: position.y
));
}
public Window window {
get { return this._windowAdapter; }
}
}
}

29
Runtime/flow/compositor_context.cs


public class ScopedFrame : IDisposable {
readonly CompositorContext _context;
readonly Canvas _canvas;
readonly bool _instrumentation_enabled;
public ScopedFrame(CompositorContext context, Canvas canvas) {
public ScopedFrame(CompositorContext context, Canvas canvas, bool instrumentation_enabled) {
this._context._beginFrame(this);
this._instrumentation_enabled = instrumentation_enabled;
this._context._beginFrame(this, this._instrumentation_enabled);
}
public CompositorContext context() {

}
public void Dispose() {
this._context._endFrame(this);
this._context._endFrame(this, this._instrumentation_enabled);
readonly Stopwatch _frameTime;
this._frameTime = new Stopwatch();
public ScopedFrame acquireFrame(Canvas canvas) {
return new ScopedFrame(this, canvas);
public ScopedFrame acquireFrame(Canvas canvas, bool instrumentation_enabled) {
return new ScopedFrame(this, canvas, instrumentation_enabled);
}
public void onGrContextCreated(Surface surface) {

return this._rasterCache;
}
void _beginFrame(ScopedFrame frame) {
public Stopwatch frameTime() {
return this._frameTime;
void _endFrame(ScopedFrame frame) {
void _beginFrame(ScopedFrame frame, bool enable_instrumentation) {
if (enable_instrumentation) {
this._frameTime.start();
}
}
void _endFrame(ScopedFrame frame, bool enable_instrumentation) {
if (enable_instrumentation) {
this._frameTime.stop();
}
}
}
}

2
Runtime/flow/layer.cs


public RasterCache rasterCache;
public float devicePixelRatio;
public Rect cullRect;
public Stopwatch frameTime;
public Stopwatch frameTime;
}
public abstract class Layer {

4
Runtime/flow/layer_tree.cs


}
static readonly Matrix3 _identityMatrix = Matrix3.I();
frameTime = frame.context().frameTime()
};
this._rootLayer.preroll(prerollContext, _identityMatrix);

var paintContext = new PaintContext {
canvas = frame.canvas(),
rasterCache = ignoreRasterCache ? null : frame.context().rasterCache(),
frameTime = frame.context().frameTime()
};
if (this._rootLayer.needsPainting) {

55
Runtime/flow/performance_overlay_layer.cs


using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.service;
using UnityEngine;
using Canvas = Unity.UIWidgets.ui.Canvas;
using Rect = Unity.UIWidgets.ui.Rect;
namespace Unity.UIWidgets.flow {
public class PerformanceOverlayLayer : Layer {

this._drawFPS(canvas, x, y);
if ((this._options & (int) PerformanceOverlayOption.drawFrameCost) == 1) {
this._drawFrameCost(canvas, x, y + fpsHeight, width, height - padding - fpsHeight);
context.frameTime.visualize(canvas,
Rect.fromLTWH(x, y + fpsHeight, width, height - padding - fpsHeight));
}
canvas.restore();

void _drawFPS(Canvas canvas, float x, float y) {
var pb = new ParagraphBuilder(new ParagraphStyle { });
pb.addText("FPS = " + PerformanceUtils.instance.getFPS());
pb.addText("FPS = " + Window.instance.getFPS());
}
void _drawFrameCost(Canvas canvas, float x, float y, float width, float height) {
Rect visualizationRect = Rect.fromLTWH(x, y, width, height);
Paint paint = new Paint {color = Colors.blue};
Paint paint2 = new Paint {color = Colors.red};
Paint paint3 = new Paint {color = Colors.green};
Paint paint4 = new Paint {color = Colors.white70};
float[] costFrames = PerformanceUtils.instance.getFrames();
int curFrame = PerformanceUtils.instance.getCurFrame();
float barWidth = Mathf.Max(1, width / costFrames.Length);
float perHeight = height / 32.0f;
canvas.drawRect(visualizationRect, paint4);
canvas.drawRect(Rect.fromLTWH(x, y + perHeight * 16.0f, width, 1), paint3);
float cur_x = x;
Path barPath = new Path();
for (var i = 0; i < costFrames.Length; i++) {
if (costFrames[i] != 0) {
float curHeight = Mathf.Min(perHeight * costFrames[i], height);
Rect barRect = Rect.fromLTWH(cur_x, y + height - curHeight, barWidth, curHeight);
barPath.addRect(barRect);
}
cur_x += barWidth;
}
canvas.drawPath(barPath, paint);
if (curFrame >= 0 && curFrame < costFrames.Length && costFrames[curFrame] != 0) {
float curHeight = Mathf.Min(perHeight * costFrames[curFrame], height);
Rect barRect = Rect.fromLTWH(x + barWidth * curFrame, y + height - curHeight, barWidth, curHeight);
canvas.drawRect(barRect, paint2);
var pb = new ParagraphBuilder(new ParagraphStyle { });
pb.addText("Frame Cost: " + costFrames[curFrame] + "ms");
var paragraph = pb.build();
paragraph.layout(new ParagraphConstraints(width: 300));
canvas.drawParagraph(paragraph, new Offset(x, y + height - 12));
}
}
}
}

3
Runtime/material/input_decorator.cs


protected override void removeChildRenderObject(RenderObject child) {
D.assert(child is RenderBox);
D.assert(this.renderObject.childToSlot.ContainsKey((RenderBox) child));
var slot = this.renderObject.childToSlot[(RenderBox) child];
D.assert(!this.renderObject.slotToChild.ContainsKey((_DecorationSlot) this.slot));
D.assert(!this.renderObject.slotToChild.ContainsKey(slot));
}
protected override void moveChildRenderObject(RenderObject child, object slotValue) {

2
Runtime/material/toggleable.cs


TickerProvider _vsync;
public bool? value {
public virtual bool? value {
get { return this._value; }
set {
D.assert(this.tristate || value != null);

3
Runtime/rendering/binding.cs


using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.service;
using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.rendering {

}
protected virtual void drawFrame() {
PerformanceUtils.instance.startProfile();
PerformanceUtils.instance.endProfile();
}
public override void hitTest(HitTestResult result, Offset position) {

35
Runtime/rendering/flex.cs


using System;
using System.Collections.Generic;
using UIWidgets.Runtime.rendering;
using Unity.UIWidgets.foundation;
using Rect = Unity.UIWidgets.ui.Rect;
namespace Unity.UIWidgets.rendering {
public enum FlexFit {

}
context.pushClipRect(this.needsCompositing, offset, Offset.zero & this.size, this.defaultPaint);
D.assert(() => {
string debugOverflowHints =
$"The overflowing {this.GetType()} has an orientation of {this._direction}.\n" +
$"The edge of the {this.GetType()} that is overflowing has been marked " +
"in the rendering with a yellow and black striped pattern. This is " +
$"usually caused by the contents being too big for the {this.GetType()}. " +
"Consider applying a flex factor (e.g. using an Expanded widget) to " +
$"force the children of the {this.GetType()} to fit within the available " +
"space instead of being sized to their natural size.\n" +
"This is considered an error condition because it indicates that there " +
"is content that cannot be seen. If the content is legitimately bigger " +
"than the available space, consider clipping it with a ClipRect widget " +
"before putting it in the flex, or using a scrollable container rather " +
"than a Flex, like a ListView.";
Rect overflowChildRect;
switch (this._direction) {
case Axis.horizontal:
overflowChildRect = Rect.fromLTWH(0.0f, 0.0f, this.size.width + this._overflow, 0.0f);
break;
case Axis.vertical:
overflowChildRect = Rect.fromLTWH(0.0f, 0.0f, 0.0f, this.size.height + this._overflow);
break;
default:
throw new Exception("Unknown direction: " + this._direction);
}
DebugOverflowIndicatorMixin.paintOverflowIndicator(this, context, offset, Offset.zero & this.size,
overflowChildRect, overflowHints: debugOverflowHints);
return true;
});
}
protected override bool hitTestChildren(HitTestResult result, Offset position = null) {

7
Runtime/rendering/shifted_box.cs


using Unity.UIWidgets.foundation;
using UIWidgets.Runtime.rendering;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;

}
context.pushClipRect(this.needsCompositing, offset, Offset.zero & this.size, base.paint);
D.assert(() => {
DebugOverflowIndicatorMixin.paintOverflowIndicator(this, context, offset, this._overflowContainerRect, this._overflowChildRect);
return true;
});
}
}

17
Runtime/ui/window.cs


using System.Collections.Generic;
using Unity.UIWidgets.async;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.service;
namespace Unity.UIWidgets.ui {
public delegate void VoidCallback();

if (value == null) {
D.assert(_instance != null, "Window.instance is already cleared.");
_instance = null;
} else {
}
else {
D.assert(_instance == null, "Window.instance is already assigned.");
_instance = value;
}

public abstract Timer runInMain(Action callback);
public abstract IDisposable getScope();
float deltaTime;
public void updateDeltaTime(float unscaledDeltaTime) {
this.deltaTime += (unscaledDeltaTime - this.deltaTime) * 0.1f;
}
public float getFPS() {
return 1.0f / this.deltaTime;
}
}
}

15
Runtime/widgets/app.cs


using System.Collections.Generic;
using RSG;
using Unity.UIWidgets.editor;
using Unity.UIWidgets.engine;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.widgets {

return provider.window;
}
public static Window of(GameObject gameObject) {
var panel = gameObject.GetComponent<UIWidgetsPanel>();
return panel == null ? null : panel.window;
}
#if UNITY_EDITOR
public static Window of(UIWidgetsEditorWindow editorWindow) {
return editorWindow.window;
}
#endif
public override bool updateShouldNotify(InheritedWidget oldWidget) {
D.assert(this.window == ((WindowProvider) oldWidget).window);

43
Runtime/widgets/basic.cs


}
}
public class UnconstrainedBox : SingleChildRenderObjectWidget {
public UnconstrainedBox(
Key key = null,
Widget child = null,
Alignment alignment = null,
Axis? constrainedAxis = null
) : base(key: key, child: child) {
this.alignment = alignment ?? Alignment.center;
this.constrainedAxis = constrainedAxis;
}
public readonly Alignment alignment;
public readonly Axis? constrainedAxis;
public override void updateRenderObject(BuildContext context, RenderObject _renderObject) {
RenderUnconstrainedBox renderObject = _renderObject as RenderUnconstrainedBox;
renderObject.alignment = this.alignment;
renderObject.constrainedAxis = this.constrainedAxis;
}
public override RenderObject createRenderObject(BuildContext context) {
return new RenderUnconstrainedBox(
alignment: this.alignment,
constrainedAxis: this.constrainedAxis
);
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<Alignment>("alignment", this.alignment));
properties.add(new DiagnosticsProperty<Axis>("constrainedAxis", null));
}
}
public class SliverToBoxAdapter : SingleChildRenderObjectWidget {
public SliverToBoxAdapter(
Key key = null,

((RenderFlex) renderObject).verticalDirection = this.verticalDirection;
((RenderFlex) renderObject).textBaseline = this.textBaseline ?? TextBaseline.alphabetic;
}
properties.add(new EnumProperty<MainAxisSize>("mainAxisSize", this.mainAxisSize, defaultValue: MainAxisSize.max));
properties.add(new EnumProperty<MainAxisSize>("mainAxisSize", this.mainAxisSize,
defaultValue: MainAxisSize.max));
properties.add(new EnumProperty<VerticalDirection>("verticalDirection", this.verticalDirection, defaultValue: VerticalDirection.down));
properties.add(new EnumProperty<VerticalDirection>("verticalDirection", this.verticalDirection,
defaultValue: VerticalDirection.down));
properties.add(new EnumProperty<TextBaseline?>("textBaseline", this.textBaseline, defaultValue: null));
}
}

2
Runtime/widgets/safe_area.cs


}
class SliverSafeArea : StatelessWidget {
public class SliverSafeArea : StatelessWidget {
public SliverSafeArea(
Key key = null,
bool left = true,

81
Runtime/widgets/sliver.cs


using System;
using System.Collections.Generic;
using System.Linq;
using com.unity.uiwidgets.Runtime.rendering;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;

return new SliverMultiBoxAdaptorElement(this);
}
public float? estimateMaxScrollOffset(
public virtual float? estimateMaxScrollOffset(
SliverConstraints constraints,
int firstIndex,
int lastIndex,

public override void updateRenderObject(BuildContext context, RenderObject renderObjectRaw) {
var renderObject = (RenderSliverFixedExtentList) renderObjectRaw;
renderObject.itemExtent = this.itemExtent;
}
}
public class SliverGrid : SliverMultiBoxAdaptorWidget {
public SliverGrid(
Key key = null,
SliverChildDelegate layoutDelegate = null,
SliverGridDelegate gridDelegate = null
) : base(key: key, del: layoutDelegate) {
D.assert(layoutDelegate != null);
D.assert(gridDelegate != null);
this.gridDelegate = gridDelegate;
}
public static SliverGrid count(
Key key = null,
int? crossAxisCount = null,
float mainAxisSpacing = 0.0f,
float crossAxisSpacing = 0.0f,
float childAspectRatio = 1.0f,
List<Widget> children = null
) {
return new SliverGrid(
key: key,
layoutDelegate: new SliverChildListDelegate(children ?? new List<Widget> { }),
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount ?? 0,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
childAspectRatio: childAspectRatio
)
);
}
public static SliverGrid extent(
Key key = null,
float? maxCrossAxisExtent = null,
float mainAxisSpacing = 0.0f,
float crossAxisSpacing = 0.0f,
float childAspectRatio = 1.0f,
List<Widget> children = null
) {
return new SliverGrid(key: key,
layoutDelegate: new SliverChildListDelegate(new List<Widget> { }),
gridDelegate: new SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: maxCrossAxisExtent ?? 0,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
childAspectRatio: childAspectRatio
));
}
public readonly SliverGridDelegate gridDelegate;
public override RenderObject createRenderObject(BuildContext context) {
SliverMultiBoxAdaptorElement element = context as SliverMultiBoxAdaptorElement;
return new RenderSliverGrid(childManager: element, gridDelegate: this.gridDelegate);
}
public override void updateRenderObject(BuildContext context, RenderObject renderObject) {
(renderObject as RenderSliverGrid).gridDelegate = this.gridDelegate;
}
public override float? estimateMaxScrollOffset(
SliverConstraints constraints,
int firstIndex,
int lastIndex,
float leadingScrollOffset,
float trailingScrollOffset
) {
return base.estimateMaxScrollOffset(
constraints,
firstIndex,
lastIndex,
leadingScrollOffset,
trailingScrollOffset
) ?? this.gridDelegate.getLayout(constraints)
.computeMaxScrollOffset(this.del.estimatedChildCount ?? 0);
}
}

70
Runtime/widgets/sliver_persistent_header.cs


bool floating = false
) : base(key: key) {
D.assert(del != null);
this.del = del;
this.layoutDelegate = del;
public readonly SliverPersistentHeaderDelegate del;
public readonly SliverPersistentHeaderDelegate layoutDelegate;
public readonly bool pinned;

if (this.floating && this.pinned) {
return new _SliverFloatingPinnedPersistentHeader(del: this.del);
return new _SliverFloatingPinnedPersistentHeader(layoutDelegate: this.layoutDelegate);
return new _SliverPinnedPersistentHeader(del: this.del);
return new _SliverPinnedPersistentHeader(layoutDelegate: this.layoutDelegate);
return new _SliverFloatingPersistentHeader(del: this.del);
return new _SliverFloatingPersistentHeader(layoutDelegate: this.layoutDelegate);
return new _SliverScrollingPersistentHeader(del: this.del);
return new _SliverScrollingPersistentHeader(layoutDelegate: this.layoutDelegate);
properties.add(new DiagnosticsProperty<SliverPersistentHeaderDelegate>("del", this.del));
properties.add(new DiagnosticsProperty<SliverPersistentHeaderDelegate>("layoutDelegate", this.layoutDelegate));
List<string> flags = new List<string> { };
if (this.pinned) {
flags.Add("pinned");

_SliverPersistentHeaderRenderObjectWidget newWidget =
_newWidget as _SliverPersistentHeaderRenderObjectWidget;
_SliverPersistentHeaderRenderObjectWidget oldWidget = this.widget;
SliverPersistentHeaderDelegate newDelegate = newWidget.del;
SliverPersistentHeaderDelegate oldDelegate = oldWidget.del;
SliverPersistentHeaderDelegate newDelegate = newWidget.layoutDelegate;
SliverPersistentHeaderDelegate oldDelegate = oldWidget.layoutDelegate;
if (newDelegate != oldDelegate &&
(newDelegate.GetType() != oldDelegate.GetType() || newDelegate.shouldRebuild(oldDelegate))) {
(this.renderObject as _RenderSliverPersistentHeaderForWidgetsMixin).triggerRebuild();

this.owner.buildScope(this,
() => {
this.child = this.updateChild(this.child,
this.widget.del.build(this, shrinkOffset, overlapsContent), null);
this.widget.layoutDelegate.build(this, shrinkOffset, overlapsContent), null);
});
}

abstract class _SliverPersistentHeaderRenderObjectWidget : RenderObjectWidget {
public _SliverPersistentHeaderRenderObjectWidget(
Key key = null,
SliverPersistentHeaderDelegate del = null
SliverPersistentHeaderDelegate layoutDelegate = null
D.assert(del != null);
this.del = del;
D.assert(layoutDelegate != null);
this.layoutDelegate = layoutDelegate;
public readonly SliverPersistentHeaderDelegate del;
public readonly SliverPersistentHeaderDelegate layoutDelegate;
public override Element createElement() {
return new _SliverPersistentHeaderElement(this);

public override void debugFillProperties(DiagnosticPropertiesBuilder description) {
base.debugFillProperties(description);
description.add(new DiagnosticsProperty<SliverPersistentHeaderDelegate>("del", this.del));
description.add(new DiagnosticsProperty<SliverPersistentHeaderDelegate>("layoutDelegate", this.layoutDelegate));
}
}

class _SliverScrollingPersistentHeader : _SliverPersistentHeaderRenderObjectWidget {
public _SliverScrollingPersistentHeader(
Key key = null,
SliverPersistentHeaderDelegate del = null
) : base(key: key, del: del) {
SliverPersistentHeaderDelegate layoutDelegate = null
) : base(key: key, layoutDelegate: layoutDelegate) {
}
public override RenderObject createRenderObject(BuildContext context) {

_SliverPersistentHeaderElement _ele;
public override float? minExtent {
get { return this._element.widget.del.minExtent; }
get { return this._element.widget.layoutDelegate.minExtent; }
get { return this._element.widget.del.maxExtent; }
get { return this._element.widget.layoutDelegate.maxExtent; }
}
protected override void updateChild(float shrinkOffset, bool overlapsContent) {

class _SliverPinnedPersistentHeader : _SliverPersistentHeaderRenderObjectWidget {
public _SliverPinnedPersistentHeader(
Key key = null,
SliverPersistentHeaderDelegate del = null
) : base(key: key, del: del) {
SliverPersistentHeaderDelegate layoutDelegate = null
) : base(key: key, layoutDelegate: layoutDelegate) {
}
public override RenderObject createRenderObject(BuildContext context) {

_SliverPersistentHeaderElement _ele;
public override float? minExtent {
get { return this._element.widget.del.minExtent; }
get { return this._element.widget.layoutDelegate.minExtent; }
get { return this._element.widget.del.maxExtent; }
get { return this._element.widget.layoutDelegate.maxExtent; }
}
protected override void updateChild(float shrinkOffset, bool overlapsContent) {

class _SliverFloatingPersistentHeader : _SliverPersistentHeaderRenderObjectWidget {
public _SliverFloatingPersistentHeader(
Key key = null,
SliverPersistentHeaderDelegate del = null
) : base(key: key, del: del) {
SliverPersistentHeaderDelegate layoutDelegate = null
) : base(key: key, layoutDelegate: layoutDelegate) {
ret.snapConfiguration = this.del.snapConfiguration;
ret.snapConfiguration = this.layoutDelegate.snapConfiguration;
return ret;
}

renderObject.snapConfiguration = this.del.snapConfiguration;
renderObject.snapConfiguration = this.layoutDelegate.snapConfiguration;
}
}

_SliverPersistentHeaderElement _ele;
public override float? minExtent {
get { return this._element.widget.del.minExtent; }
get { return this._element.widget.layoutDelegate.minExtent; }
get { return this._element.widget.del.maxExtent; }
get { return this._element.widget.layoutDelegate.maxExtent; }
}
protected override void updateChild(float shrinkOffset, bool overlapsContent) {

class _SliverFloatingPinnedPersistentHeader : _SliverPersistentHeaderRenderObjectWidget {
public _SliverFloatingPinnedPersistentHeader(
Key key = null,
SliverPersistentHeaderDelegate del = null
) : base(key: key, del: del) {
SliverPersistentHeaderDelegate layoutDelegate = null
) : base(key: key, layoutDelegate: layoutDelegate) {
ret.snapConfiguration = this.del.snapConfiguration;
ret.snapConfiguration = this.layoutDelegate.snapConfiguration;
return ret;
}

renderObject.snapConfiguration = this.del.snapConfiguration;
renderObject.snapConfiguration = this.layoutDelegate.snapConfiguration;
}
}

_SliverPersistentHeaderElement _ele;
public override float? minExtent {
get { return this._element.widget.del.minExtent; }
get { return this._element.widget.layoutDelegate.minExtent; }
get { return this._element.widget.del.maxExtent; }
get { return this._element.widget.layoutDelegate.maxExtent; }
}
protected override void updateChild(float shrinkOffset, bool overlapsContent) {

2
Runtime/widgets/text.cs


textAlign: this.textAlign ?? defaultTextStyle.textAlign ?? TextAlign.left,
softWrap: this.softWrap ?? defaultTextStyle.softWrap,
overflow: this.overflow ?? defaultTextStyle.overflow,
textScaleFactor: this.textScaleFactor ?? 1.0f, // MediaQuery.textScaleFactorOf(context), todo
textScaleFactor: this.textScaleFactor ?? MediaQuery.textScaleFactorOf(context),
maxLines: this.maxLines ?? defaultTextStyle.maxLines,
text: new TextSpan(
style: effectiveTextStyle,

4
Samples/UIWidgetsGallery/gallery/demo.cs


void _showApiDocumentation(BuildContext context) {
string url = this.demos[DefaultTabController.of(context).index].documentationUrl;
if (url != null) {
// TODO: find Unity equivalent
// Open the URL in browser
// launch(url, forceWebView: true);
Application.OpenURL(url);
}
}

18
Samples/UIWidgetsGallery/gallery/demos.cs


public static partial class DemoUtils {
static List<GalleryDemo> _buildGalleryDemos() {
List<GalleryDemo> galleryDemos = new List<GalleryDemo> {
// // Demos
// new GalleryDemo(
// title: "Shrine",
// subtitle: "Basic shopping app",
// icon: GalleryIcons.shrine,
// category: GalleryDemoCategory._kDemos,
// routeName: ShrineDemo.routeName,
// buildRoute: (BuildContext context) => new ShrineDemo()
// ),
// Demos
new GalleryDemo(
title: "Shrine",
subtitle: "Basic shopping app",
icon: GalleryIcons.shrine,
category: DemoUtils._kDemos,
routeName: ShrineDemo.routeName,
buildRoute: (BuildContext context) => new ShrineDemo()
),
new GalleryDemo(
title: "Contact profile",
subtitle: "Address book entry with a flexible appbar",

2
Runtime/flow/instrumentation.cs.meta


fileFormatVersion: 2
guid: 819a2117a730b431bb069817e3ed8cd7
guid: 89eef64f138164ea0a407cf8e65f8186
MonoImporter:
externalObjects: {}
serializedVersion: 2

34
Runtime/Plugins/platform/ios/UIWidgetsViewController.h


#ifndef PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSVIEWCONTROLLER_H_
#define PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSVIEWCONTROLLER_H_
#import <UIKit/UIKit.h>
#include "UIWidgetsTextInputDelegate.h"
struct viewPadding
{
float top;
float bottom;
float left;
float right;
};
struct viewMetrics
{
float insets_top;
float insets_bottom;
float insets_left;
float insets_right;
float padding_top;
float padding_bottom;
float padding_left;
float padding_right;
};
@interface UIWidgetsViewController : NSObject
@property viewPadding padding;
@property viewPadding viewInsets;
@end
#endif // PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSVIEWCONTROLLER_H_

26
Runtime/Plugins/platform/ios/UIWidgetsViewController.h.meta


fileFormatVersion: 2
guid: a797fb0a2ddb84d4e8ac7f6da5d70819
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

108
Runtime/Plugins/platform/ios/UIWidgetsViewController.mm


#include "UIWidgetsViewController.h"
#include "UIWidgetsMessageManager.h"
#include <Foundation/Foundation.h>
#include <UIKit/UIKit.h>
@implementation UIWidgetsViewController {
}
@synthesize viewInsets;
@synthesize padding;
- (instancetype)init {
self = [super init];
if (self) {
viewInsets.bottom = 0;
viewInsets.top = 0;
viewInsets.left = 0;
viewInsets.right = 0;
CGFloat scale = [[UIScreen mainScreen] scale];
if (@available(iOS 11, *)) {
padding.bottom = [UIApplication sharedApplication].keyWindow.safeAreaInsets.bottom * scale;
padding.top = [UIApplication sharedApplication].keyWindow.safeAreaInsets.top * scale;
padding.left = [UIApplication sharedApplication].keyWindow.safeAreaInsets.left * scale;
padding.right = [UIApplication sharedApplication].keyWindow.safeAreaInsets.right * scale;
} else {
CGRect statusFrame = [UIApplication sharedApplication].statusBarFrame;
padding.bottom = 0;
padding.top = statusFrame.size.height * scale;
padding.left = 0;
padding.right = 0;
}
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(keyboardWillChangeFrame:)
name:UIKeyboardWillChangeFrameNotification
object:nil];
[center addObserver:self
selector:@selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification
object:nil];
}
return self;
}
-(void)keyboardWillBeHidden:(NSNotification*)notification {
viewInsets.bottom = 0;
CGFloat scale = [UIScreen mainScreen].scale;
if (@available(iOS 11, *)) {
CGFloat cur_padding = [UIApplication sharedApplication].keyWindow.safeAreaInsets.bottom * scale;
padding.bottom = cur_padding;
} else {
CGRect statusFrame = [UIApplication sharedApplication].statusBarFrame;
CGFloat cur_padding = statusFrame.size.height * scale;
padding.top = cur_padding;
}
UIWidgetsMethodMessage(@"ViewportMatricsChanged", @"UIWidgetViewController.keyboardHide", @[]);
}
-(void)keyboardWillChangeFrame:(NSNotification*)notification {
NSDictionary* info = [notification userInfo];
CGFloat bottom = CGRectGetHeight([[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]);
CGFloat scale = [UIScreen mainScreen].scale;
viewInsets.bottom = bottom * scale;
padding.bottom = 0;
UIWidgetsMethodMessage(@"ViewportMatricsChanged", @"UIWidgetViewController.keyboardHide", @[]);
}
-(void)tryLaunch {
}
+ (instancetype)sharedInstance {
static UIWidgetsViewController *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[UIWidgetsViewController alloc] init];
});
return sharedInstance;
}
@end
extern "C"
{
viewMetrics IOSGetViewportPadding()
{
viewMetrics metrics;
viewPadding insets = [[UIWidgetsViewController sharedInstance] viewInsets];
viewPadding padding = [[UIWidgetsViewController sharedInstance] padding];
metrics.insets_bottom = insets.bottom;
metrics.insets_top = insets.top;
metrics.insets_left = insets.left;
metrics.insets_right = insets.right;
metrics.padding_bottom = padding.bottom;
metrics.padding_top = padding.top;
metrics.padding_left = padding.left;
metrics.padding_right = padding.right;
return metrics;
}
}

36
Runtime/Plugins/platform/ios/UIWidgetsViewController.mm.meta


fileFormatVersion: 2
guid: fc3d8a9d616f147929ae9b8898fa01bf
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings: {}
- first:
tvOS: tvOS
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

113
Runtime/flow/instrumentation.cs


using Unity.UIWidgets.material;
using Unity.UIWidgets.ui;
using UnityEditor;
using UnityEngine;
using Canvas = Unity.UIWidgets.ui.Canvas;
using Rect = Unity.UIWidgets.ui.Rect;
namespace Unity.UIWidgets.flow {
static class InstrumentationUtils {
public const int kMaxSamples = 120;
public static float now() {
#if UNITY_EDITOR
return (float) EditorApplication.timeSinceStartup;
#else
return Time.realtimeSinceStartup;
#endif
}
}
public class Stopwatch {
float _start;
float[] _laps;
int _currentSample;
bool _cacheDirty;
public Stopwatch() {
this._start = InstrumentationUtils.now();
this._currentSample = 0;
float delta = 0f;
this._laps = new float[InstrumentationUtils.kMaxSamples];
for (int i = 0; i < this._laps.Length; i++) {
this._laps[i] = delta;
}
this._cacheDirty = true;
}
public void start() {
this._start = InstrumentationUtils.now();
this._currentSample = (this._currentSample + 1) % InstrumentationUtils.kMaxSamples;
}
public void stop() {
this._laps[this._currentSample] = InstrumentationUtils.now() - this._start;
}
public void setLapTime(float delta) {
this._currentSample = (this._currentSample + 1) % InstrumentationUtils.kMaxSamples;
this._laps[this._currentSample] = delta;
}
public float lastLap() {
return this._laps[(this._currentSample - 1) % InstrumentationUtils.kMaxSamples];
}
public float maxDelta() {
float maxDelta = 0f;
for (int i = 0; i < this._laps.Length; i++) {
if (maxDelta < this._laps[i]) {
maxDelta = this._laps[i];
}
}
return maxDelta;
}
public void visualize(Canvas canvas, Rect rect) {
Paint paint = new Paint {color = Colors.blue};
Paint paint2 = new Paint {color = Colors.red};
Paint paint3 = new Paint {color = Colors.green};
Paint paint4 = new Paint {color = Colors.white70};
float[] costFrames = this._laps;
int curFrame = (this._currentSample - 1) % InstrumentationUtils.kMaxSamples;
float barWidth = Mathf.Max(1, rect.width / costFrames.Length);
float perHeight = rect.height / 32.0f;
canvas.drawRect(rect, paint4);
canvas.drawRect(Rect.fromLTWH(rect.left, rect.top + perHeight * 16.0f, rect.width, 1), paint3);
float cur_x = rect.left;
Path barPath = new Path();
for (var i = 0; i < costFrames.Length; i++) {
if (costFrames[i] != 0) {
float curHeight = Mathf.Min(perHeight * costFrames[i] * 1000, rect.height);
Rect barRect = Rect.fromLTWH(cur_x, rect.top + rect.height - curHeight, barWidth, curHeight);
barPath.addRect(barRect);
}
cur_x += barWidth;
}
canvas.drawPath(barPath, paint);
if (curFrame >= 0 && curFrame < costFrames.Length && costFrames[curFrame] != 0) {
float curHeight = Mathf.Min(perHeight * costFrames[curFrame] * 1000, rect.height);
Rect barRect = Rect.fromLTWH(rect.left + barWidth * curFrame, rect.top + rect.height - curHeight,
barWidth, curHeight);
canvas.drawRect(barRect, paint2);
var pb = new ParagraphBuilder(new ParagraphStyle { });
pb.addText("Current Frame Cost: " + costFrames[curFrame] * 1000 + "ms" + " ; Max(in last 120 frames): " + this.maxDelta() * 1000 + "ms");
var paragraph = pb.build();
paragraph.layout(new ParagraphConstraints(width: 800));
canvas.drawParagraph(paragraph, new Offset(rect.left, rect.top + rect.height - 12));
}
}
}
}

310
Runtime/material/checkbox.cs


using System;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Canvas = Unity.UIWidgets.ui.Canvas;
using Color = Unity.UIWidgets.ui.Color;
using Rect = Unity.UIWidgets.ui.Rect;
namespace Unity.UIWidgets.material {
class CheckboxUtils {
public const float _kEdgeSize = Checkbox.width;
public static readonly Radius _kEdgeRadius = Radius.circular(1.0f);
public const float _kStrokeWidth = 2.0f;
}
public class Checkbox : StatefulWidget {
public Checkbox(
Key key = null,
bool? value = false,
bool tristate = false,
ValueChanged<bool?> onChanged = null,
Color activeColor = null,
Color checkColor = null,
MaterialTapTargetSize? materialTapTargetSize = null
) : base(key: key) {
D.assert(tristate || value != null);
this.value = value;
this.onChanged = onChanged;
this.activeColor = activeColor;
this.checkColor = checkColor;
this.tristate = tristate;
this.materialTapTargetSize = materialTapTargetSize;
}
public readonly bool? value;
public readonly ValueChanged<bool?> onChanged;
public readonly Color activeColor;
public readonly Color checkColor;
public readonly bool tristate;
public readonly MaterialTapTargetSize? materialTapTargetSize;
public const float width = 18.0f;
public override State createState() {
return new _CheckboxState();
}
}
class _CheckboxState : TickerProviderStateMixin<Checkbox> {
public override Widget build(BuildContext context) {
D.assert(MaterialD.debugCheckHasMaterial(context));
ThemeData themeData = Theme.of(context);
Size size;
switch (this.widget.materialTapTargetSize ?? themeData.materialTapTargetSize) {
case MaterialTapTargetSize.padded:
size = new Size(2 * Constants.kRadialReactionRadius + 8.0f,
2 * Constants.kRadialReactionRadius + 8.0f);
break;
case MaterialTapTargetSize.shrinkWrap:
size = new Size(2 * Constants.kRadialReactionRadius, 2 * Constants.kRadialReactionRadius);
break;
default:
throw new Exception("Unknown target size: " + this.widget.materialTapTargetSize);
}
BoxConstraints additionalConstraints = BoxConstraints.tight(size);
return new _CheckboxRenderObjectWidget(
value: this.widget.value,
tristate: this.widget.tristate,
activeColor: this.widget.activeColor ?? themeData.toggleableActiveColor,
checkColor: this.widget.checkColor ?? new Color(0xFFFFFFFF),
inactiveColor: this.widget.onChanged != null
? themeData.unselectedWidgetColor
: themeData.disabledColor,
onChanged: this.widget.onChanged,
additionalConstraints: additionalConstraints,
vsync: this
);
}
}
class _CheckboxRenderObjectWidget : LeafRenderObjectWidget {
public _CheckboxRenderObjectWidget(
Key key = null,
bool? value = null,
bool tristate = false,
Color activeColor = null,
Color checkColor = null,
Color inactiveColor = null,
ValueChanged<bool?> onChanged = null,
TickerProvider vsync = null,
BoxConstraints additionalConstraints = null
) : base(key: key) {
D.assert(tristate || value != null);
D.assert(activeColor != null);
D.assert(inactiveColor != null);
D.assert(vsync != null);
this.value = value;
this.tristate = tristate;
this.activeColor = activeColor;
this.checkColor = checkColor;
this.inactiveColor = inactiveColor;
this.onChanged = onChanged;
this.vsync = vsync;
this.additionalConstraints = additionalConstraints;
}
public readonly bool? value;
public readonly bool tristate;
public readonly Color activeColor;
public readonly Color checkColor;
public readonly Color inactiveColor;
public readonly ValueChanged<bool?> onChanged;
public readonly TickerProvider vsync;
public readonly BoxConstraints additionalConstraints;
public override RenderObject createRenderObject(BuildContext context) {
return new _RenderCheckbox(
value: this.value,
tristate: this.tristate,
activeColor: this.activeColor,
checkColor: this.checkColor,
inactiveColor: this.inactiveColor,
onChanged: this.onChanged,
vsync: this.vsync,
additionalConstraints: this.additionalConstraints
);
}
public override void updateRenderObject(BuildContext context, RenderObject _renderObject) {
_RenderCheckbox renderObject = _renderObject as _RenderCheckbox;
renderObject.value = this.value;
renderObject.tristate = this.tristate;
renderObject.activeColor = this.activeColor;
renderObject.checkColor = this.checkColor;
renderObject.inactiveColor = this.inactiveColor;
renderObject.onChanged = this.onChanged;
renderObject.additionalConstraints = this.additionalConstraints;
renderObject.vsync = this.vsync;
}
}
class _RenderCheckbox : RenderToggleable {
public _RenderCheckbox(
bool? value = null,
bool tristate = false,
Color activeColor = null,
Color checkColor = null,
Color inactiveColor = null,
BoxConstraints additionalConstraints = null,
ValueChanged<bool?> onChanged = null,
TickerProvider vsync = null
) : base(
value: value,
tristate: tristate,
activeColor: activeColor,
inactiveColor: inactiveColor,
onChanged: onChanged,
additionalConstraints: additionalConstraints,
vsync: vsync
) {
this._oldValue = value;
this.checkColor = checkColor;
}
bool? _oldValue;
public Color checkColor;
public override bool? value {
set {
if (value == this.value) {
return;
}
this._oldValue = this.value;
base.value = value;
}
}
RRect _outerRectAt(Offset origin, float t) {
float inset = 1.0f - (t - 0.5f).abs() * 2.0f;
float size = CheckboxUtils._kEdgeSize - inset * CheckboxUtils._kStrokeWidth;
Rect rect = Rect.fromLTWH(origin.dx + inset, origin.dy + inset, size, size);
return RRect.fromRectAndRadius(rect, CheckboxUtils._kEdgeRadius);
}
Color _colorAt(float t) {
return this.onChanged == null
? this.inactiveColor
: (t >= 0.25f ? this.activeColor : Color.lerp(this.inactiveColor, this.activeColor, t * 4.0f));
}
void _initStrokePaint(Paint paint) {
paint.color = this.checkColor;
paint.style = PaintingStyle.stroke;
paint.strokeWidth = CheckboxUtils._kStrokeWidth;
}
void _drawBorder(Canvas canvas, RRect outer, float t, Paint paint) {
D.assert(t >= 0.0f && t <= 0.5f);
float size = outer.width;
RRect inner = outer.deflate(Mathf.Min(size / 2.0f, CheckboxUtils._kStrokeWidth + size * t));
canvas.drawDRRect(outer, inner, paint);
}
void _drawCheck(Canvas canvas, Offset origin, float t, Paint paint) {
D.assert(t >= 0.0f && t <= 1.0f);
Path path = new Path();
Offset start = new Offset(CheckboxUtils._kEdgeSize * 0.15f, CheckboxUtils._kEdgeSize * 0.45f);
Offset mid = new Offset(CheckboxUtils._kEdgeSize * 0.4f, CheckboxUtils._kEdgeSize * 0.7f);
Offset end = new Offset(CheckboxUtils._kEdgeSize * 0.85f, CheckboxUtils._kEdgeSize * 0.25f);
if (t < 0.5f) {
float strokeT = t * 2.0f;
Offset drawMid = Offset.lerp(start, mid, strokeT);
path.moveTo(origin.dx + start.dx, origin.dy + start.dy);
path.lineTo(origin.dx + drawMid.dx, origin.dy + drawMid.dy);
}
else {
float strokeT = (t - 0.5f) * 2.0f;
Offset drawEnd = Offset.lerp(mid, end, strokeT);
path.moveTo(origin.dx + start.dx, origin.dy + start.dy);
path.lineTo(origin.dx + mid.dx, origin.dy + mid.dy);
path.lineTo(origin.dx + drawEnd.dx, origin.dy + drawEnd.dy);
}
canvas.drawPath(path, paint);
}
void _drawDash(Canvas canvas, Offset origin, float t, Paint paint) {
D.assert(t >= 0.0f && t <= 1.0f);
Offset start = new Offset(CheckboxUtils._kEdgeSize * 0.2f, CheckboxUtils._kEdgeSize * 0.5f);
Offset mid = new Offset(CheckboxUtils._kEdgeSize * 0.5f, CheckboxUtils._kEdgeSize * 0.5f);
Offset end = new Offset(CheckboxUtils._kEdgeSize * 0.8f, CheckboxUtils._kEdgeSize * 0.5f);
Offset drawStart = Offset.lerp(start, mid, 1.0f - t);
Offset drawEnd = Offset.lerp(mid, end, t);
canvas.drawLine(origin + drawStart, origin + drawEnd, paint);
}
public override void paint(PaintingContext context, Offset offset) {
Canvas canvas = context.canvas;
this.paintRadialReaction(canvas, offset, this.size.center(Offset.zero));
Offset origin = offset + (this.size / 2.0f - Size.square(CheckboxUtils._kEdgeSize) / 2.0f);
AnimationStatus status = this.position.status;
float tNormalized = status == AnimationStatus.forward || status == AnimationStatus.completed
? this.position.value
: 1.0f - this.position.value;
if (this._oldValue == false || this.value == false) {
float t = this.value == false ? 1.0f - tNormalized : tNormalized;
RRect outer = this._outerRectAt(origin, t);
Paint paint = new Paint();
paint.color = this._colorAt(t);
if (t <= 0.5f) {
this._drawBorder(canvas, outer, t, paint);
}
else {
canvas.drawRRect(outer, paint);
this._initStrokePaint(paint);
float tShrink = (t - 0.5f) * 2.0f;
if (this._oldValue == null || this.value == null) {
this._drawDash(canvas, origin, tShrink, paint);
}
else {
this._drawCheck(canvas, origin, tShrink, paint);
}
}
}
else {
// Two cases: null to true, true to null
RRect outer = this._outerRectAt(origin, 1.0f);
Paint paint = new Paint();
paint.color = this._colorAt(1.0f);
canvas.drawRRect(outer, paint);
this._initStrokePaint(paint);
if (tNormalized <= 0.5f) {
float tShrink = 1.0f - tNormalized * 2.0f;
if (this._oldValue == true) {
this._drawCheck(canvas, origin, tShrink, paint);
}
else {
this._drawDash(canvas, origin, tShrink, paint);
}
}
else {
float tExpand = (tNormalized - 0.5f) * 2.0f;
if (this.value == true) {
this._drawCheck(canvas, origin, tExpand, paint);
}
else {
this._drawDash(canvas, origin, tExpand, paint);
}
}
}
}
}
}

3
Runtime/material/checkbox.cs.meta


fileFormatVersion: 2
guid: 3f506995b69c421cbdd95746b4ca2182
timeCreated: 1554213056

275
Runtime/rendering/debug_overflow_indicator.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using Gradient = Unity.UIWidgets.ui.Gradient;
using Rect = Unity.UIWidgets.ui.Rect;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace UIWidgets.Runtime.rendering {
enum _OverflowSide {
left,
top,
bottom,
right
}
class _OverflowRegionData {
public _OverflowRegionData(
Rect rect = null,
string label = "",
Offset labelOffset = null,
float rotation = 0.0f,
_OverflowSide? side = null
) {
this.rect = rect;
this.label = label;
this.labelOffset = labelOffset ?? Offset.zero;
this.rotation = rotation;
this.side = side;
}
public readonly Rect rect;
public readonly string label;
public readonly Offset labelOffset;
public readonly float rotation;
public readonly _OverflowSide? side;
}
public static class DebugOverflowIndicatorMixin {
static readonly Color _black = new Color(0xBF000000);
static readonly Color _yellow = new Color(0xBFFFFF00);
const float _indicatorFraction = 0.1f;
const float _indicatorFontSizePixels = 7.5f;
const float _indicatorLabelPaddingPixels = 1.0f;
static readonly TextStyle _indicatorTextStyle = new TextStyle(
color: new Color(0xFF900000),
fontSize: _indicatorFontSizePixels,
fontWeight: FontWeight.w800
);
static readonly Paint _indicatorPaint = new Paint();
static readonly Paint _labelBackgroundPaint = new Paint();
static readonly List<TextPainter> _indicatorLabel = new List<TextPainter>(4);
static DebugOverflowIndicatorMixin() {
_indicatorPaint.shader = Gradient.linear(
new Offset(0.0f, 0.0f),
new Offset(10.0f, 10.0f),
new List<Color> {_black, _yellow, _yellow, _black},
new List<float> {0.25f, 0.25f, 0.75f, 0.75f},
TileMode.repeated
);
_labelBackgroundPaint.color = new Color(0xFFFFFFFF);
for (int i = 0; i < 4; i++) {
_indicatorLabel.Add(new TextPainter(new TextSpan(""), textDirection: TextDirection.ltr));
}
}
static readonly Dictionary<RenderObject, bool> _overflowReportNeeded = new Dictionary<RenderObject, Boolean>();
static string _formatPixels(float value) {
D.assert(value > 0.0f);
string pixels;
if (value > 10.0f) {
pixels = value.ToString("0");
}
else if (value > 1.0f) {
pixels = value.ToString("0.0");
}
else {
pixels = value.ToString("0.000");
}
return pixels;
}
static List<_OverflowRegionData> _calculateOverflowRegions(RelativeRect overflow, Rect containerRect) {
List<_OverflowRegionData> regions = new List<_OverflowRegionData> { };
if (overflow.left > 0.0f) {
Rect markerRect = Rect.fromLTWH(
0.0f,
0.0f,
containerRect.width * _indicatorFraction,
containerRect.height
);
regions.Add(new _OverflowRegionData(
rect: markerRect,
label: "LEFT OVERFLOWED BY ${_formatPixels(overflow.left)} PIXELS",
labelOffset: markerRect.centerLeft +
new Offset(_indicatorFontSizePixels + _indicatorLabelPaddingPixels, 0.0f),
rotation: Mathf.PI / 2.0f,
side: _OverflowSide.left
));
}
if (overflow.right > 0.0f) {
Rect markerRect = Rect.fromLTWH(
containerRect.width * (1.0f - _indicatorFraction),
0.0f,
containerRect.width * _indicatorFraction,
containerRect.height
);
regions.Add(new _OverflowRegionData(
rect: markerRect,
label: $"RIGHT OVERFLOWED BY {_formatPixels(overflow.right)} PIXELS",
labelOffset: markerRect.centerRight -
new Offset(_indicatorFontSizePixels + _indicatorLabelPaddingPixels, 0.0f),
rotation: -Mathf.PI / 2.0f,
side: _OverflowSide.right
));
}
if (overflow.top > 0.0f) {
Rect markerRect = Rect.fromLTWH(
0.0f,
0.0f,
containerRect.width,
containerRect.height * _indicatorFraction
);
regions.Add(new _OverflowRegionData(
rect: markerRect,
label: $"TOP OVERFLOWED BY {_formatPixels(overflow.top)} PIXELS",
labelOffset: markerRect.topCenter + new Offset(0.0f, _indicatorLabelPaddingPixels),
rotation: 0.0f,
side: _OverflowSide.top
));
}
if (overflow.bottom > 0.0f) {
Rect markerRect = Rect.fromLTWH(
0.0f,
containerRect.height * (1.0f - _indicatorFraction),
containerRect.width,
containerRect.height * _indicatorFraction
);
regions.Add(new _OverflowRegionData(
rect: markerRect,
label: $"BOTTOM OVERFLOWED BY {_formatPixels(overflow.bottom)} PIXELS",
labelOffset: markerRect.bottomCenter -
new Offset(0.0f, _indicatorFontSizePixels + _indicatorLabelPaddingPixels),
rotation: 0.0f,
side: _OverflowSide.bottom
));
}
return regions;
}
static void _reportOverflow(RenderObject renderObject, RelativeRect overflow, string overflowHints) {
overflowHints = overflowHints ?? $"The edge of the {renderObject.GetType()} that is " +
"overflowing has been marked in the rendering with a yellow and black " +
"striped pattern. This is usually caused by the contents being too big " +
$"for the {renderObject.GetType()}.\n" +
"This is considered an error condition because it indicates that there " +
"is content that cannot be seen. If the content is legitimately bigger " +
"than the available space, consider clipping it with a ClipRect widget " +
$"before putting it in the {renderObject.GetType()}, or using a scrollable " +
"container, like a ListView.";
List<string> overflows = new List<string> { };
if (overflow.left > 0.0f) {
overflows.Add($"{_formatPixels(overflow.left)} pixels on the left");
}
if (overflow.top > 0.0f) {
overflows.Add($"{_formatPixels(overflow.top)} pixels on the top");
}
if (overflow.bottom > 0.0f) {
overflows.Add($"{_formatPixels(overflow.bottom)} pixels on the bottom");
}
if (overflow.right > 0.0f) {
overflows.Add($"{_formatPixels(overflow.right)} pixels on the right");
}
string overflowText = "";
D.assert(overflows.isNotEmpty(),
$"Somehow {renderObject.GetType()} didn't actually overflow like it thought it did.");
switch (overflows.Count) {
case 1:
overflowText = overflows.first();
break;
case 2:
overflowText = $"{overflows.first()} and {overflows.last()}";
break;
default:
overflows[overflows.Count - 1] = $"and {overflows[overflows.Count - 1]}";
overflowText = string.Join(", ", overflow);
break;
}
UIWidgetsError.reportError(
new UIWidgetsErrorDetails(
exception: new Exception($"A {renderObject.GetType()} overflowed by {overflowText}."),
library: "rendering library",
context: "during layout",
informationCollector: (information) => {
information.AppendLine(overflowHints);
information.AppendLine($"The specific {renderObject.GetType()} in question is:");
information.AppendLine($" {renderObject.toStringShallow(joiner: "\n ")}");
information.AppendLine(string.Concat(Enumerable.Repeat("◢◤", 32)));
}
)
);
}
public static void paintOverflowIndicator(
RenderObject renderObject,
PaintingContext context,
Offset offset,
Rect containerRect,
Rect childRect,
string overflowHints = null
) {
RelativeRect overflow = RelativeRect.fromRect(containerRect, childRect);
if (overflow.left <= 0.0f &&
overflow.right <= 0.0f &&
overflow.top <= 0.0f &&
overflow.bottom <= 0.0f) {
return;
}
List<_OverflowRegionData> overflowRegions = _calculateOverflowRegions(overflow, containerRect);
foreach (_OverflowRegionData region in overflowRegions) {
context.canvas.drawRect(region.rect.shift(offset), _indicatorPaint);
if (_indicatorLabel[(int) region.side].text?.text != region.label) {
_indicatorLabel[(int) region.side].text = new TextSpan(
text: region.label,
style: _indicatorTextStyle
);
_indicatorLabel[(int) region.side].layout();
}
Offset labelOffset = region.labelOffset + offset;
Offset centerOffset = new Offset(-_indicatorLabel[(int) region.side].width / 2.0f, 0.0f);
Rect textBackgroundRect = centerOffset & _indicatorLabel[(int) region.side].size;
context.canvas.save();
context.canvas.translate(labelOffset.dx, labelOffset.dy);
context.canvas.rotate(region.rotation);
context.canvas.drawRect(textBackgroundRect, _labelBackgroundPaint);
_indicatorLabel[(int) region.side].paint(context.canvas, centerOffset);
context.canvas.restore();
}
bool containsKey = _overflowReportNeeded.TryGetValue(renderObject, out var overflowReportNeeded);
overflowReportNeeded |= !containsKey;
if (overflowReportNeeded) {
_overflowReportNeeded[renderObject] = false;
_reportOverflow(renderObject, overflow, overflowHints);
}
}
}
}

11
Runtime/rendering/debug_overflow_indicator.cs.meta


fileFormatVersion: 2
guid: 3d9d2d2974274fa39fc3caac8ee159d1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

451
Runtime/rendering/sliver_grid.cs


using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using UnityEngine;
namespace com.unity.uiwidgets.Runtime.rendering {
public class SliverGridGeometry {
public SliverGridGeometry(
float? scrollOffset = null,
float? crossAxisOffset = null,
float? mainAxisExtent = null,
float? crossAxisExtent = null
) {
this.scrollOffset = scrollOffset;
this.crossAxisOffset = crossAxisOffset;
this.mainAxisExtent = mainAxisExtent;
this.crossAxisExtent = crossAxisExtent;
}
public readonly float? scrollOffset;
public readonly float? crossAxisOffset;
public readonly float? mainAxisExtent;
public readonly float? crossAxisExtent;
public float? trailingScrollOffset {
get { return this.scrollOffset + this.mainAxisExtent; }
}
public BoxConstraints getBoxConstraints(SliverConstraints constraints) {
return constraints.asBoxConstraints(
minExtent: this.mainAxisExtent ?? 0.0f,
maxExtent: this.mainAxisExtent ?? 0.0f,
crossAxisExtent: this.crossAxisExtent ?? 0.0f
);
}
public override string ToString() {
return "SliverGridGeometry(" +
"scrollOffset: $scrollOffset, " +
"crossAxisOffset: $crossAxisOffset, " +
"mainAxisExtent: $mainAxisExtent, " +
"crossAxisExtent: $crossAxisExtent" +
")";
}
}
public abstract class SliverGridLayout {
public SliverGridLayout() {
}
public abstract int getMinChildIndexForScrollOffset(float scrollOffset);
public abstract int getMaxChildIndexForScrollOffset(float scrollOffset);
public abstract SliverGridGeometry getGeometryForChildIndex(int index);
public abstract float computeMaxScrollOffset(int childCount);
}
public class SliverGridRegularTileLayout : SliverGridLayout {
public SliverGridRegularTileLayout(
int? crossAxisCount = null,
float? mainAxisStride = null,
float? crossAxisStride = null,
float? childMainAxisExtent = null,
float? childCrossAxisExtent = null,
bool? reverseCrossAxis = null
) {
D.assert(crossAxisCount != null && crossAxisCount > 0);
D.assert(mainAxisStride != null && mainAxisStride >= 0);
D.assert(crossAxisStride != null && crossAxisStride >= 0);
D.assert(childMainAxisExtent != null && childMainAxisExtent >= 0);
D.assert(childCrossAxisExtent != null && childCrossAxisExtent >= 0);
D.assert(reverseCrossAxis != null);
this.crossAxisCount = crossAxisCount;
this.mainAxisStride = mainAxisStride;
this.crossAxisStride = crossAxisStride;
this.childMainAxisExtent = childMainAxisExtent;
this.childCrossAxisExtent = childCrossAxisExtent;
this.reverseCrossAxis = reverseCrossAxis;
}
public readonly int? crossAxisCount;
public readonly float? mainAxisStride;
public readonly float? crossAxisStride;
public readonly float? childMainAxisExtent;
public readonly float? childCrossAxisExtent;
public readonly bool? reverseCrossAxis;
public override int getMinChildIndexForScrollOffset(float scrollOffset) {
return (this.mainAxisStride > 0.0f
? this.crossAxisCount * ((int) (scrollOffset / this.mainAxisStride))
: 0) ?? 0;
}
public override int getMaxChildIndexForScrollOffset(float scrollOffset) {
if (this.mainAxisStride > 0.0f) {
int? mainAxisCount = (scrollOffset / this.mainAxisStride)?.ceil();
return Mathf.Max(0, (this.crossAxisCount * mainAxisCount - 1) ?? 0);
}
return 0;
}
float _getOffsetFromStartInCrossAxis(float crossAxisStart) {
if (this.reverseCrossAxis == true) {
return (this.crossAxisCount * this.crossAxisStride - crossAxisStart - this.childCrossAxisExtent) ??
0.0f;
}
return crossAxisStart;
}
public override SliverGridGeometry getGeometryForChildIndex(int index) {
float? crossAxisStart = (index % this.crossAxisCount) * this.crossAxisStride;
return new SliverGridGeometry(
scrollOffset: (index / this.crossAxisCount) * this.mainAxisStride,
crossAxisOffset:
this._getOffsetFromStartInCrossAxis(crossAxisStart ?? 0.0f),
mainAxisExtent:
this.childMainAxisExtent,
crossAxisExtent:
this.childCrossAxisExtent
);
}
public override float computeMaxScrollOffset(int childCount) {
int? mainAxisCount = ((childCount - 1) / this.crossAxisCount) + 1;
float? mainAxisSpacing = this.mainAxisStride - this.childMainAxisExtent;
return (this.mainAxisStride * mainAxisCount - mainAxisSpacing) ?? 0.0f;
}
}
public abstract class SliverGridDelegate {
public abstract SliverGridLayout getLayout(SliverConstraints constraints);
public abstract bool shouldRelayout(SliverGridDelegate oldDelegate);
}
public class SliverGridDelegateWithFixedCrossAxisCount : SliverGridDelegate {
public SliverGridDelegateWithFixedCrossAxisCount(
int crossAxisCount,
float mainAxisSpacing = 0.0f,
float crossAxisSpacing = 0.0f,
float childAspectRatio = 1.0f
) {
D.assert(crossAxisCount > 0);
D.assert(mainAxisSpacing >= 0);
D.assert(crossAxisSpacing >= 0);
D.assert(childAspectRatio > 0);
this.crossAxisCount = crossAxisCount;
this.mainAxisSpacing = mainAxisSpacing;
this.crossAxisSpacing = crossAxisSpacing;
this.childAspectRatio = childAspectRatio;
}
public readonly int crossAxisCount;
public readonly float mainAxisSpacing;
public readonly float crossAxisSpacing;
public readonly float childAspectRatio;
bool _debugAssertIsValid() {
D.assert(this.crossAxisCount > 0);
D.assert(this.mainAxisSpacing >= 0.0f);
D.assert(this.crossAxisSpacing >= 0.0f);
D.assert(this.childAspectRatio > 0.0f);
return true;
}
public override SliverGridLayout getLayout(SliverConstraints constraints) {
D.assert(this._debugAssertIsValid());
float usableCrossAxisExtent =
constraints.crossAxisExtent - this.crossAxisSpacing * (this.crossAxisCount - 1);
float childCrossAxisExtent = usableCrossAxisExtent / this.crossAxisCount;
float childMainAxisExtent = childCrossAxisExtent / this.childAspectRatio;
return new SliverGridRegularTileLayout(
crossAxisCount: this.crossAxisCount,
mainAxisStride: childMainAxisExtent + this.mainAxisSpacing,
crossAxisStride: childCrossAxisExtent + this.crossAxisSpacing,
childMainAxisExtent: childMainAxisExtent,
childCrossAxisExtent: childCrossAxisExtent,
reverseCrossAxis: AxisUtils.axisDirectionIsReversed(constraints.crossAxisDirection)
);
}
public override bool shouldRelayout(SliverGridDelegate _oldDelegate) {
SliverGridDelegateWithFixedCrossAxisCount oldDelegate =
_oldDelegate as SliverGridDelegateWithFixedCrossAxisCount;
return oldDelegate.crossAxisCount != this.crossAxisCount
|| oldDelegate.mainAxisSpacing != this.mainAxisSpacing
|| oldDelegate.crossAxisSpacing != this.crossAxisSpacing
|| oldDelegate.childAspectRatio != this.childAspectRatio;
}
}
public class SliverGridDelegateWithMaxCrossAxisExtent : SliverGridDelegate {
public SliverGridDelegateWithMaxCrossAxisExtent(
float maxCrossAxisExtent,
float mainAxisSpacing = 0.0f,
float crossAxisSpacing = 0.0f,
float childAspectRatio = 1.0f
) {
D.assert(maxCrossAxisExtent >= 0);
D.assert(mainAxisSpacing >= 0);
D.assert(crossAxisSpacing >= 0);
D.assert(childAspectRatio > 0);
this.maxCrossAxisExtent = maxCrossAxisExtent;
this.mainAxisSpacing = mainAxisSpacing;
this.crossAxisSpacing = crossAxisSpacing;
this.childAspectRatio = childAspectRatio;
}
public readonly float maxCrossAxisExtent;
public readonly float mainAxisSpacing;
public readonly float crossAxisSpacing;
public readonly float childAspectRatio;
bool _debugAssertIsValid() {
D.assert(this.maxCrossAxisExtent > 0.0f);
D.assert(this.mainAxisSpacing >= 0.0f);
D.assert(this.crossAxisSpacing >= 0.0f);
D.assert(this.childAspectRatio > 0.0f);
return true;
}
public override SliverGridLayout getLayout(SliverConstraints constraints) {
D.assert(this._debugAssertIsValid());
int crossAxisCount =
(constraints.crossAxisExtent / (this.maxCrossAxisExtent + this.crossAxisSpacing)).ceil();
float usableCrossAxisExtent = constraints.crossAxisExtent - this.crossAxisSpacing * (crossAxisCount - 1);
float childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
float childMainAxisExtent = childCrossAxisExtent / this.childAspectRatio;
return new SliverGridRegularTileLayout(
crossAxisCount: crossAxisCount,
mainAxisStride: childMainAxisExtent + this.mainAxisSpacing,
crossAxisStride: childCrossAxisExtent + this.crossAxisSpacing,
childMainAxisExtent: childMainAxisExtent,
childCrossAxisExtent: childCrossAxisExtent,
reverseCrossAxis: AxisUtils.axisDirectionIsReversed(constraints.crossAxisDirection)
);
}
public override bool shouldRelayout(SliverGridDelegate _oldDelegate) {
SliverGridDelegateWithMaxCrossAxisExtent oldDelegate =
_oldDelegate as SliverGridDelegateWithMaxCrossAxisExtent;
return oldDelegate.maxCrossAxisExtent != this.maxCrossAxisExtent
|| oldDelegate.mainAxisSpacing != this.mainAxisSpacing
|| oldDelegate.crossAxisSpacing != this.crossAxisSpacing
|| oldDelegate.childAspectRatio != this.childAspectRatio;
}
}
public class SliverGridParentData : SliverMultiBoxAdaptorParentData {
public float crossAxisOffset;
public override string ToString() {
return "crossAxisOffset=$crossAxisOffset; ${base.ToString()}";
}
}
public class RenderSliverGrid : RenderSliverMultiBoxAdaptor {
public RenderSliverGrid(
RenderSliverBoxChildManager childManager,
SliverGridDelegate gridDelegate
) : base(childManager: childManager) {
D.assert(gridDelegate != null);
this._gridDelegate = gridDelegate;
}
public override void setupParentData(RenderObject child) {
if (!(child.parentData is SliverGridParentData)) {
child.parentData = new SliverGridParentData();
}
}
public SliverGridDelegate gridDelegate {
get { return this._gridDelegate; }
set {
D.assert(value != null);
if (this._gridDelegate == value) {
return;
}
if (value.GetType() != this._gridDelegate.GetType() ||
value.shouldRelayout(this._gridDelegate)) {
this.markNeedsLayout();
}
this._gridDelegate = value;
}
}
SliverGridDelegate _gridDelegate;
public override float childCrossAxisPosition(RenderObject child) {
SliverGridParentData childParentData = (SliverGridParentData) child.parentData;
return childParentData.crossAxisOffset;
}
protected override void performLayout() {
this.childManager.didStartLayout();
this.childManager.setDidUnderflow(false);
float scrollOffset = this.constraints.scrollOffset + this.constraints.cacheOrigin;
D.assert(scrollOffset >= 0.0f);
float remainingExtent = this.constraints.remainingCacheExtent;
D.assert(remainingExtent >= 0.0f);
float targetEndScrollOffset = scrollOffset + remainingExtent;
SliverGridLayout layout = this._gridDelegate.getLayout(this.constraints);
int firstIndex = layout.getMinChildIndexForScrollOffset(scrollOffset);
int? targetLastIndex = targetEndScrollOffset.isFinite()
? (int?) layout.getMaxChildIndexForScrollOffset(targetEndScrollOffset)
: null;
if (this.firstChild != null) {
int oldFirstIndex = this.indexOf(this.firstChild);
int oldLastIndex = this.indexOf(this.lastChild);
int leadingGarbage = (firstIndex - oldFirstIndex).clamp(0, this.childCount);
int trailingGarbage = targetLastIndex == null
? 0
: ((oldLastIndex - targetLastIndex) ?? 0).clamp(0, this.childCount);
this.collectGarbage(leadingGarbage, trailingGarbage);
}
else {
this.collectGarbage(0, 0);
}
SliverGridGeometry firstChildGridGeometry = layout.getGeometryForChildIndex(firstIndex);
float? leadingScrollOffset = firstChildGridGeometry.scrollOffset;
float? trailingScrollOffset = firstChildGridGeometry.trailingScrollOffset;
if (this.firstChild == null) {
if (!this.addInitialChild(index: firstIndex,
layoutOffset: firstChildGridGeometry.scrollOffset ?? 0.0f)) {
float max = layout.computeMaxScrollOffset(this.childManager.childCount ?? 0);
this.geometry = new SliverGeometry(
scrollExtent: max,
maxPaintExtent: max
);
this.childManager.didFinishLayout();
return;
}
}
RenderBox trailingChildWithLayout = null;
for (int index = this.indexOf(this.firstChild) - 1; index >= firstIndex; --index) {
SliverGridGeometry gridGeometry = layout.getGeometryForChildIndex(index);
RenderBox child = this.insertAndLayoutLeadingChild(
gridGeometry.getBoxConstraints(this.constraints)
);
SliverGridParentData childParentData = child.parentData as SliverGridParentData;
childParentData.layoutOffset = gridGeometry.scrollOffset ?? 0.0f;
childParentData.crossAxisOffset = gridGeometry.crossAxisOffset ?? 0.0f;
D.assert(childParentData.index == index);
trailingChildWithLayout = trailingChildWithLayout ?? child;
trailingScrollOffset =
Mathf.Max(trailingScrollOffset ?? 0.0f, gridGeometry.trailingScrollOffset ?? 0.0f);
}
if (trailingChildWithLayout == null) {
this.firstChild.layout(firstChildGridGeometry.getBoxConstraints(this.constraints));
SliverGridParentData childParentData = this.firstChild.parentData as SliverGridParentData;
childParentData.layoutOffset = firstChildGridGeometry.scrollOffset ?? 0.0f;
childParentData.crossAxisOffset = firstChildGridGeometry.crossAxisOffset ?? 0.0f;
trailingChildWithLayout = this.firstChild;
}
for (int index = this.indexOf(trailingChildWithLayout) + 1;
targetLastIndex == null || index <= targetLastIndex;
++index) {
SliverGridGeometry gridGeometry = layout.getGeometryForChildIndex(index);
BoxConstraints childConstraints = gridGeometry.getBoxConstraints(this.constraints);
RenderBox child = this.childAfter(trailingChildWithLayout);
if (child == null) {
child = this.insertAndLayoutChild(childConstraints, after: trailingChildWithLayout);
if (child == null) {
break;
}
}
else {
child.layout(childConstraints);
}
trailingChildWithLayout = child;
D.assert(child != null);
SliverGridParentData childParentData = child.parentData as SliverGridParentData;
childParentData.layoutOffset = gridGeometry.scrollOffset ?? 0.0f;
childParentData.crossAxisOffset = gridGeometry.crossAxisOffset ?? 0.0f;
D.assert(childParentData.index == index);
trailingScrollOffset =
Mathf.Max(trailingScrollOffset ?? 0.0f, gridGeometry.trailingScrollOffset ?? 0.0f);
}
int lastIndex = this.indexOf(this.lastChild);
D.assert(this.childScrollOffset(this.firstChild) <= scrollOffset);
D.assert(this.debugAssertChildListIsNonEmptyAndContiguous());
D.assert(this.indexOf(this.firstChild) == firstIndex);
D.assert(targetLastIndex == null || lastIndex <= targetLastIndex);
float estimatedTotalExtent = this.childManager.estimateMaxScrollOffset(this.constraints,
firstIndex: firstIndex,
lastIndex: lastIndex,
leadingScrollOffset: leadingScrollOffset ?? 0.0f,
trailingScrollOffset: trailingScrollOffset ?? 0.0f
);
float paintExtent = this.calculatePaintOffset(this.constraints,
from: leadingScrollOffset ?? 0.0f,
to: trailingScrollOffset ?? 0.0f
);
float cacheExtent = this.calculateCacheOffset(this.constraints,
from: leadingScrollOffset ?? 0.0f,
to: trailingScrollOffset ?? 0.0f
);
this.geometry = new SliverGeometry(
scrollExtent: estimatedTotalExtent,
paintExtent: paintExtent,
maxPaintExtent: estimatedTotalExtent,
cacheExtent: cacheExtent,
hasVisualOverflow: true
);
if (estimatedTotalExtent == trailingScrollOffset) {
this.childManager.setDidUnderflow(true);
}
this.childManager.didFinishLayout();
}
}
}

11
Runtime/rendering/sliver_grid.cs.meta


fileFormatVersion: 2
guid: 5ba43ca1896804e9b8ca548384ee2abc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

553
Runtime/widgets/heroes.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.widgets {
public delegate Tween<Rect> CreateRectTween(Rect begin, Rect end);
public delegate Widget HeroFlightShuttleBuilder(
BuildContext flightContext,
Animation<float> animation,
HeroFlightDirection flightDirection,
BuildContext fromHeroContext,
BuildContext toHeroContext
);
delegate void _OnFlightEnded(_HeroFlight flight);
public enum HeroFlightDirection {
push,
pop
}
class HeroUtils {
public static Rect _globalBoundingBoxFor(BuildContext context) {
RenderBox box = (RenderBox) context.findRenderObject();
D.assert(box != null && box.hasSize);
return box.getTransformTo(null).mapRect(Offset.zero & box.size);
}
}
public class Hero : StatefulWidget {
public Hero(
Key key = null,
object tag = null,
CreateRectTween createRectTween = null,
HeroFlightShuttleBuilder flightShuttleBuilder = null,
TransitionBuilder placeholderBuilder = null,
bool transitionOnUserGestures = false,
Widget child = null
) : base(key: key) {
D.assert(tag != null);
D.assert(child != null);
this.tag = tag;
this.createRectTween = createRectTween;
this.child = child;
this.flightShuttleBuilder = flightShuttleBuilder;
this.placeholderBuilder = placeholderBuilder;
this.transitionOnUserGestures = transitionOnUserGestures;
}
public readonly object tag;
public readonly CreateRectTween createRectTween;
public readonly Widget child;
public readonly HeroFlightShuttleBuilder flightShuttleBuilder;
public readonly TransitionBuilder placeholderBuilder;
public readonly bool transitionOnUserGestures;
internal static Dictionary<object, _HeroState>
_allHeroesFor(BuildContext context, bool isUserGestureTransition) {
D.assert(context != null);
Dictionary<object, _HeroState> result = new Dictionary<object, _HeroState> { };
void visitor(Element element) {
if (element.widget is Hero) {
StatefulElement hero = (StatefulElement) element;
Hero heroWidget = (Hero) element.widget;
if (!isUserGestureTransition || heroWidget.transitionOnUserGestures) {
object tag = heroWidget.tag;
D.assert(tag != null);
D.assert(() => {
if (result.ContainsKey(tag)) {
throw new UIWidgetsError(
"There are multiple heroes that share the same tag within a subtree.\n" +
"Within each subtree for which heroes are to be animated (typically a PageRoute subtree), " +
"each Hero must have a unique non-null tag.\n" +
$"In this case, multiple heroes had the following tag: {tag}\n" +
"Here is the subtree for one of the offending heroes:\n" +
$"{element.toStringDeep(prefixLineOne: "# ")}"
);
}
return true;
});
_HeroState heroState = (_HeroState) hero.state;
result[tag] = heroState;
}
}
element.visitChildren(visitor);
}
context.visitChildElements(visitor);
return result;
}
public override State createState() {
return new _HeroState();
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<object>("tag", this.tag));
}
}
class _HeroState : State<Hero> {
GlobalKey _key = GlobalKey.key();
Size _placeholderSize;
public void startFlight() {
D.assert(this.mounted);
RenderBox box = (RenderBox) this.context.findRenderObject();
D.assert(box != null && box.hasSize);
this.setState(() => { this._placeholderSize = box.size; });
}
public void endFlight() {
if (this.mounted) {
this.setState(() => { this._placeholderSize = null; });
}
}
public override Widget build(BuildContext context) {
if (this._placeholderSize != null) {
if (this.widget.placeholderBuilder == null) {
return new SizedBox(
width: this._placeholderSize.width,
height: this._placeholderSize.height
);
}
else {
return this.widget.placeholderBuilder(context, this.widget.child);
}
}
return new KeyedSubtree(
key: this._key,
child: this.widget.child
);
}
}
class _HeroFlightManifest {
public _HeroFlightManifest(
HeroFlightDirection type,
OverlayState overlay,
Rect navigatorRect,
PageRoute fromRoute,
PageRoute toRoute,
_HeroState fromHero,
_HeroState toHero,
CreateRectTween createRectTween,
HeroFlightShuttleBuilder shuttleBuilder,
bool isUserGestureTransition
) {
D.assert(this.fromHero.widget.tag == this.toHero.widget.tag);
this.type = type;
this.overlay = overlay;
this.navigatorRect = navigatorRect;
this.fromRoute = fromRoute;
this.toRoute = toRoute;
this.fromHero = fromHero;
this.toHero = toHero;
this.createRectTween = createRectTween;
this.shuttleBuilder = shuttleBuilder;
this.isUserGestureTransition = isUserGestureTransition;
}
public readonly HeroFlightDirection type;
public readonly OverlayState overlay;
public readonly Rect navigatorRect;
public readonly PageRoute fromRoute;
public readonly PageRoute toRoute;
public readonly _HeroState fromHero;
public readonly _HeroState toHero;
public readonly CreateRectTween createRectTween;
public readonly HeroFlightShuttleBuilder shuttleBuilder;
public readonly bool isUserGestureTransition;
public object tag {
get { return this.fromHero.widget.tag; }
}
public Animation<float> animation {
get {
return new CurvedAnimation(
parent: (this.type == HeroFlightDirection.push) ? this.toRoute.animation : this.fromRoute.animation,
curve: Curves.fastOutSlowIn
);
}
}
public override string ToString() {
return $"_HeroFlightManifest($type tag: $tag from route: {this.fromRoute.settings} " +
$"to route: {this.toRoute.settings} with hero: {this.fromHero} to {this.toHero})";
}
}
class _HeroFlight {
public _HeroFlight(_OnFlightEnded onFlightEnded) {
this.onFlightEnded = onFlightEnded;
this._proxyAnimation = new ProxyAnimation();
this._proxyAnimation.addStatusListener(this._handleAnimationUpdate);
}
public readonly _OnFlightEnded onFlightEnded;
Tween<Rect> heroRectTween;
Widget shuttle;
Animation<float> _heroOpacity = Animations.kAlwaysCompleteAnimation;
ProxyAnimation _proxyAnimation;
public _HeroFlightManifest manifest;
public OverlayEntry overlayEntry;
bool _aborted = false;
Tween<Rect> _doCreateRectTween(Rect begin, Rect end) {
CreateRectTween createRectTween =
this.manifest.toHero.widget.createRectTween ?? this.manifest.createRectTween;
if (createRectTween != null) {
return createRectTween(begin, end);
}
return new RectTween(begin: begin, end: end);
}
static readonly Animatable<float> _reverseTween = new FloatTween(begin: 1.0f, end: 0.0f);
Widget _buildOverlay(BuildContext context) {
D.assert(this.manifest != null);
this.shuttle = this.shuttle ?? this.manifest.shuttleBuilder(
context, this.manifest.animation, this.manifest.type, this.manifest.fromHero.context,
this.manifest.toHero.context
);
D.assert(this.shuttle != null);
return new AnimatedBuilder(
animation: this._proxyAnimation,
child: this.shuttle,
builder: (BuildContext _, Widget child) => {
RenderBox toHeroBox = (RenderBox) this.manifest.toHero.context?.findRenderObject();
if (this._aborted || toHeroBox == null || !toHeroBox.attached) {
if (this._heroOpacity.isCompleted) {
this._heroOpacity = this._proxyAnimation.drive(
_reverseTween.chain(
new CurveTween(curve: new Interval(this._proxyAnimation.value, 1.0f)))
);
}
}
else if (toHeroBox.hasSize) {
RenderBox finalRouteBox = (RenderBox) this.manifest.toRoute.subtreeContext?.findRenderObject();
Offset toHeroOrigin = toHeroBox.localToGlobal(Offset.zero, ancestor: finalRouteBox);
if (toHeroOrigin != this.heroRectTween.end.topLeft) {
Rect heroRectEnd = toHeroOrigin & this.heroRectTween.end.size;
this.heroRectTween = this._doCreateRectTween(this.heroRectTween.begin, heroRectEnd);
}
}
Rect rect = this.heroRectTween.evaluate(this._proxyAnimation);
Size size = this.manifest.navigatorRect.size;
RelativeRect offsets = RelativeRect.fromSize(rect, size);
return new Positioned(
top: offsets.top,
right: offsets.right,
bottom: offsets.bottom,
left: offsets.left,
child: new IgnorePointer(
child: new RepaintBoundary(
child: new Opacity(
opacity: this._heroOpacity.value,
child: child
)
)
)
);
}
);
}
void _handleAnimationUpdate(AnimationStatus status) {
if (status == AnimationStatus.completed || status == AnimationStatus.dismissed) {
this._proxyAnimation.parent = null;
D.assert(this.overlayEntry != null);
this.overlayEntry.remove();
this.overlayEntry = null;
this.manifest.fromHero.endFlight();
this.manifest.toHero.endFlight();
this.onFlightEnded(this);
}
}
public void start(_HeroFlightManifest initialManifest) {
D.assert(!this._aborted);
D.assert(() => {
Animation<float> initial = initialManifest.animation;
D.assert(initial != null);
HeroFlightDirection type = initialManifest.type;
switch (type) {
case HeroFlightDirection.pop:
return initial.value == 1.0f && initialManifest.isUserGestureTransition
? initial.status == AnimationStatus.completed
: initial.status == AnimationStatus.reverse;
case HeroFlightDirection.push:
return initial.value == 0.0f && initial.status == AnimationStatus.forward;
}
throw new Exception("Unknown type: " + type);
});
this.manifest = initialManifest;
if (this.manifest.type == HeroFlightDirection.pop) {
this._proxyAnimation.parent = new ReverseAnimation(this.manifest.animation);
}
else {
this._proxyAnimation.parent = this.manifest.animation;
}
this.manifest.fromHero.startFlight();
this.manifest.toHero.startFlight();
this.heroRectTween = this._doCreateRectTween(
HeroUtils._globalBoundingBoxFor(this.manifest.fromHero.context),
HeroUtils._globalBoundingBoxFor(this.manifest.toHero.context)
);
this.overlayEntry = new OverlayEntry(builder: this._buildOverlay);
this.manifest.overlay.insert(this.overlayEntry);
}
public void divert(_HeroFlightManifest newManifest) {
D.assert(this.manifest.tag == newManifest.tag);
if (this.manifest.type == HeroFlightDirection.push && newManifest.type == HeroFlightDirection.pop) {
D.assert(newManifest.animation.status == AnimationStatus.reverse);
D.assert(this.manifest.fromHero == newManifest.toHero);
D.assert(this.manifest.toHero == newManifest.fromHero);
D.assert(this.manifest.fromRoute == newManifest.toRoute);
D.assert(this.manifest.toRoute == newManifest.fromRoute);
this._proxyAnimation.parent = new ReverseAnimation(newManifest.animation);
this.heroRectTween = new ReverseTween<Rect>(this.heroRectTween);
}
else if (this.manifest.type == HeroFlightDirection.pop && newManifest.type == HeroFlightDirection.push) {
D.assert(newManifest.animation.status == AnimationStatus.forward);
D.assert(this.manifest.toHero == newManifest.fromHero);
D.assert(this.manifest.toRoute == newManifest.fromRoute);
this._proxyAnimation.parent = newManifest.animation.drive(
new FloatTween(
begin: this.manifest.animation.value,
end: 1.0f
)
);
if (this.manifest.fromHero != newManifest.toHero) {
this.manifest.fromHero.endFlight();
newManifest.toHero.startFlight();
this.heroRectTween = this._doCreateRectTween(this.heroRectTween.end,
HeroUtils._globalBoundingBoxFor(newManifest.toHero.context));
}
else {
this.heroRectTween = this._doCreateRectTween(this.heroRectTween.end, this.heroRectTween.begin);
}
}
else {
D.assert(this.manifest.fromHero != newManifest.fromHero);
D.assert(this.manifest.toHero != newManifest.toHero);
this.heroRectTween = this._doCreateRectTween(this.heroRectTween.evaluate(this._proxyAnimation),
HeroUtils._globalBoundingBoxFor(newManifest.toHero.context));
this.shuttle = null;
if (newManifest.type == HeroFlightDirection.pop) {
this._proxyAnimation.parent = new ReverseAnimation(newManifest.animation);
}
else {
this._proxyAnimation.parent = newManifest.animation;
}
this.manifest.fromHero.endFlight();
this.manifest.toHero.endFlight();
newManifest.fromHero.startFlight();
newManifest.toHero.startFlight();
this.overlayEntry.markNeedsBuild();
}
this._aborted = false;
this.manifest = newManifest;
}
public void abort() {
this._aborted = true;
}
public override string ToString() {
RouteSettings from = this.manifest.fromRoute.settings;
RouteSettings to = this.manifest.toRoute.settings;
object tag = this.manifest.tag;
return "HeroFlight(for: $tag, from: $from, to: $to ${_proxyAnimation.parent})";
}
}
public class HeroController : NavigatorObserver {
HeroController(CreateRectTween createRectTween) {
this.createRectTween = createRectTween;
}
public readonly CreateRectTween createRectTween;
Dictionary<object, _HeroFlight> _flights = new Dictionary<object, _HeroFlight>();
public override void didPush(Route route, Route previousRoute) {
D.assert(this.navigator != null);
D.assert(route != null);
this._maybeStartHeroTransition(previousRoute, route, HeroFlightDirection.push, false);
}
public override void didPop(Route route, Route previousRoute) {
D.assert(this.navigator != null);
D.assert(route != null);
this._maybeStartHeroTransition(route, previousRoute, HeroFlightDirection.pop, false);
}
public override void didStartUserGesture(Route route, Route previousRoute) {
D.assert(this.navigator != null);
D.assert(route != null);
this._maybeStartHeroTransition(route, previousRoute, HeroFlightDirection.pop, true);
}
void _maybeStartHeroTransition(
Route fromRoute,
Route toRoute,
HeroFlightDirection flightType,
bool isUserGestureTransition
) {
if (toRoute != fromRoute && toRoute is PageRoute && fromRoute is PageRoute) {
PageRoute from = (PageRoute) fromRoute;
PageRoute to = (PageRoute) toRoute;
Animation<float> animation = (flightType == HeroFlightDirection.push) ? to.animation : from.animation;
switch (flightType) {
case HeroFlightDirection.pop:
if (animation.value == 0.0f) {
return;
}
break;
case HeroFlightDirection.push:
if (animation.value == 1.0f) {
return;
}
break;
}
if (isUserGestureTransition && flightType == HeroFlightDirection.pop && to.maintainState) {
this._startHeroTransition(from, to, animation, flightType, isUserGestureTransition);
}
else {
to.offstage = to.animation.value == 0.0f;
WidgetsBinding.instance.addPostFrameCallback((TimeSpan value) => {
this._startHeroTransition(from, to, animation, flightType, isUserGestureTransition);
});
}
}
}
void _startHeroTransition(
PageRoute from,
PageRoute to,
Animation<float> animation,
HeroFlightDirection flightType,
bool isUserGestureTransition
) {
if (this.navigator == null || from.subtreeContext == null || to.subtreeContext == null) {
to.offstage = false; // in case we set this in _maybeStartHeroTransition
return;
}
Rect navigatorRect = HeroUtils._globalBoundingBoxFor(this.navigator.context);
Dictionary<object, _HeroState>
fromHeroes = Hero._allHeroesFor(from.subtreeContext, isUserGestureTransition);
Dictionary<object, _HeroState> toHeroes = Hero._allHeroesFor(to.subtreeContext, isUserGestureTransition);
to.offstage = false;
foreach (object tag in fromHeroes.Keys) {
if (toHeroes[tag] != null) {
HeroFlightShuttleBuilder fromShuttleBuilder = fromHeroes[tag].widget.flightShuttleBuilder;
HeroFlightShuttleBuilder toShuttleBuilder = toHeroes[tag].widget.flightShuttleBuilder;
_HeroFlightManifest manifest = new _HeroFlightManifest(
type: flightType,
overlay: this.navigator.overlay,
navigatorRect: navigatorRect,
fromRoute: from,
toRoute: to,
fromHero: fromHeroes[tag],
toHero: toHeroes[tag],
createRectTween: this.createRectTween,
shuttleBuilder:
toShuttleBuilder ?? fromShuttleBuilder ?? _defaultHeroFlightShuttleBuilder,
isUserGestureTransition: isUserGestureTransition
);
if (this._flights[tag] != null) {
this._flights[tag].divert(manifest);
}
else {
this._flights[tag] = new _HeroFlight(this._handleFlightEnded);
this._flights[tag].start(manifest);
}
}
else if (this._flights[tag] != null) {
this._flights[tag].abort();
}
}
}
void _handleFlightEnded(_HeroFlight flight) {
this._flights.Remove(flight.manifest.tag);
}
static readonly HeroFlightShuttleBuilder _defaultHeroFlightShuttleBuilder = (
BuildContext flightContext,
Animation<float> animation,
HeroFlightDirection flightDirection,
BuildContext fromHeroContext,
BuildContext toHeroContext
) => {
Hero toHero = (Hero) toHeroContext.widget;
return toHero.child;
};
}
}

3
Runtime/widgets/heroes.cs.meta


fileFormatVersion: 2
guid: ae21d125110c4c989fd2ce9e5a0bc1c0
timeCreated: 1554263028

63
Samples/UIWidgetSample/HttpRequestSample.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.widgets;
using UnityEngine;
using UnityEngine.Networking;
public class HttpRequestSample : UIWidgetsPanel
{
protected override Widget createWidget() {
return new MaterialApp(
title: "Http Request Sample",
home: new Scaffold(
body:new AsyncRequestWidget(this.gameObject)
)
);
}
}
public class AsyncRequestWidget : StatefulWidget {
public readonly GameObject gameObjOfUIWidgetsPanel;
public AsyncRequestWidget(GameObject gameObjOfUiWidgetsPanel, Key key = null) : base(key) {
this.gameObjOfUIWidgetsPanel = gameObjOfUiWidgetsPanel;
}
public override State createState() {
return new _AsyncRequestWidgetState();
}
}
[Serializable]
public class TimeData {
public long currentFileTime;
}
class _AsyncRequestWidgetState : State<AsyncRequestWidget> {
long _fileTime;
public override Widget build(BuildContext context) {
return new Column(
children: new List<Widget>() {
new FlatButton(child: new Text("Click To Get Time"), onPressed: () => {
UnityWebRequest www = UnityWebRequest.Get("http://worldclockapi.com/api/json/est/now");
var asyncOperation = www.SendWebRequest();
asyncOperation.completed += operation => {
var timeData = JsonUtility.FromJson<TimeData>(www.downloadHandler.text);
using(WindowProvider.of(this.widget.gameObjOfUIWidgetsPanel).getScope())
{
this.setState(() => { this._fileTime = timeData.currentFileTime; });
}
};
}),
new Text($"current file time: {this._fileTime}")
});
}
}

11
Samples/UIWidgetSample/HttpRequestSample.cs.meta


fileFormatVersion: 2
guid: cfa3e1cd78bb74aef90a7a0289dfc23c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Samples/UIWidgetsGallery/demo/shrine.meta


fileFormatVersion: 2
guid: 2621a6f94ad8a4b5ab7199025967dc90
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

40
Samples/UIWidgetsGallery/demo/shrine_demo.cs


using Unity.UIWidgets.animation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery.gallery {
class ShrineDemoUtils {
public static Widget buildShrine(BuildContext context, Widget child) {
return new Theme(
data: new ThemeData(
primarySwatch: Colors.grey,
iconTheme: new IconThemeData(color: new Color(0xFF707070)),
platform: Theme.of(context).platform
),
child: new ShrineTheme(child: child)
);
}
}
public class ShrinePageRoute<T> : MaterialPageRoute {
public ShrinePageRoute(
WidgetBuilder builder,
RouteSettings settings
) : base(builder: builder, settings: settings) {
}
public override Widget buildPage(BuildContext context, Animation<float> animation,
Animation<float> secondaryAnimation) {
return ShrineDemoUtils.buildShrine(context, base.buildPage(context, animation, secondaryAnimation));
}
}
public class ShrineDemo : StatelessWidget {
public const string routeName = "/shrine";
public override Widget build(BuildContext context) {
return ShrineDemoUtils.buildShrine(context, new ShrineHome());
}
}
}

3
Samples/UIWidgetsGallery/demo/shrine_demo.cs.meta


fileFormatVersion: 2
guid: 5902905cca7447d2aa1fe5ae7f6c5cbc
timeCreated: 1553239620

8
Tests/Resources/people.meta


fileFormatVersion: 2
guid: 8988da6435b0e414da67d5bb45802754
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

280
Samples/UIWidgetsGallery/demo/shrine/shrine_data.cs


using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.foundation;
namespace UIWidgetsGallery.gallery {
class ShrineData {
const string _kGalleryAssetsPackage = "flutter_gallery_assets";
static Vendor _ali = new Vendor(
name: "Ali’s shop",
avatarAsset: "people/square/ali",
avatarAssetPackage: _kGalleryAssetsPackage,
description:
"Ali Connor’s makes custom goods for folks of all shapes and sizes " +
"made by hand and sometimes by machine, but always with love and care. " +
"Custom orders are available upon request if you need something extra special."
);
static Vendor _peter = new Vendor(
name: "Peter’s shop",
avatarAsset: "people/square/peter",
avatarAssetPackage: _kGalleryAssetsPackage,
description:
"Peter makes great stuff for awesome people like you. Super cool and extra " +
"awesome all of his shop’s goods are handmade with love. Custom orders are " +
"available upon request if you need something extra special."
);
static Vendor _sandra = new Vendor(
name: "Sandra’s shop",
avatarAsset: "people/square/sandra",
avatarAssetPackage: _kGalleryAssetsPackage,
description:
"Sandra specializes in furniture, beauty and travel products with a classic vibe. " +
"Custom orders are available if you’re looking for a certain color or material."
);
static Vendor _stella = new Vendor(
name: "Stella’s shop",
avatarAsset: "people/square/stella",
avatarAssetPackage: _kGalleryAssetsPackage,
description:
"Stella sells awesome stuff at lovely prices. made by hand and sometimes by " +
"machine, but always with love and care. Custom orders are available upon request " +
"if you need something extra special."
);
static Vendor _trevor = new Vendor(
name: "Trevor’s shop",
avatarAsset: "people/square/trevor",
avatarAssetPackage: _kGalleryAssetsPackage,
description:
"Trevor makes great stuff for awesome people like you. Super cool and extra " +
"awesome all of his shop’s goods are handmade with love. Custom orders are " +
"available upon request if you need something extra special."
);
static readonly List<Product> _allProducts = new List<Product> {
new Product(
name: "Vintage Brown Belt",
imageAsset: "products/belt",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"fashion", "latest"},
price: 300.00f,
vendor: _sandra,
description:
"Isn’t it cool when things look old, but they're not. Looks Old But Not makes " +
"awesome vintage goods that are base smart. This ol’ belt just got an upgrade. "
),
new Product(
name: "Sunglasses",
imageAsset: "products/sunnies",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"travel", "fashion", "beauty"},
price: 20.00f,
vendor: _trevor,
description:
"Be an optimist. Carry Sunglasses with you at all times. All Tints and " +
"Shades products come with polarized lenses and base duper UV protection " +
"so you can look at the sun for however long you want. Sunglasses make you " +
"look cool, wear them."
),
new Product(
name: "Flatwear",
imageAsset: "products/flatwear",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"furniture"},
price: 30.00f,
vendor: _trevor,
description:
"Leave the tunnel and the rain is fallin amazing things happen when you wait"
),
new Product(
name: "Salmon Sweater",
imageAsset: "products/sweater",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"fashion"},
price: 300.00f,
vendor: _stella,
description:
"Looks can be deceiving. This sweater comes in a wide variety of " +
"flavors, including salmon, that pop as soon as they hit your eyes. " +
"Sweaters heat quickly, so savor the warmth."
),
new Product(
name: "Pine Table",
imageAsset: "products/table",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"furniture"},
price: 63.00f,
vendor: _stella,
description:
"Leave the tunnel and the rain is fallin amazing things happen when you wait"
),
new Product(
name: "Green Comfort Jacket",
imageAsset: "products/jacket",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"fashion"},
price: 36.00f,
vendor: _ali,
description:
"Leave the tunnel and the rain is fallin amazing things happen when you wait"
),
new Product(
name: "Chambray Top",
imageAsset: "products/top",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"fashion"},
price: 125.00f,
vendor: _peter,
description:
"Leave the tunnel and the rain is fallin amazing things happen when you wait"
),
new Product(
name: "Blue Cup",
imageAsset: "products/cup",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"travel", "furniture"},
price: 75.00f,
vendor: _sandra,
description:
"Drinksy has been making extraordinary mugs for decades. With each " +
"cup purchased Drinksy donates a cup to those in need. Buy yourself a mug, " +
"buy someone else a mug."
),
new Product(
name: "Tea Set",
imageAsset: "products/teaset",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"furniture", "fashion"},
price: 70.00f,
vendor: _trevor,
featureTitle: "Beautiful glass teapot",
featureDescription:
"Teapot holds extremely hot liquids and pours them from the spout.",
description:
"Impress your guests with Tea Set by Kitchen Stuff. Teapot holds extremely " +
"hot liquids and pours them from the spout. Use the handle, shown on the right, " +
"so your fingers don’t get burnt while pouring."
),
new Product(
name: "Blue linen napkins",
imageAsset: "products/napkins",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"furniture", "fashion"},
price: 89.00f,
vendor: _trevor,
description:
"Blue linen napkins were meant to go with friends, so you may want to pick " +
"up a bunch of these. These things are absorbant."
),
new Product(
name: "Dipped Earrings",
imageAsset: "products/earrings",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"fashion", "beauty"},
price: 25.00f,
vendor: _stella,
description:
"WeDipIt does it again. These hand-dipped 4 inch earrings are perfect for " +
"the office or the beach. Just be sure you don’t drop it in a bucket of " +
"red paint, then they won’t look dipped anymore."
),
new Product(
name: "Perfect Planters",
imageAsset: "products/planters",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"latest", "furniture"},
price: 30.00f,
vendor: _ali,
description:
"The Perfect Planter Co makes the best vessels for just about anything you " +
"can pot. This set of Perfect Planters holds succulents and cuttings perfectly. " +
"Looks great in any room. Keep out of reach from cats."
),
new Product(
name: "Cloud-White Dress",
imageAsset: "products/dress",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"fashion"},
price: 54.00f,
vendor: _sandra,
description:
"Trying to find the perfect outift to match your mood? Try no longer. " +
"This Cloud-White Dress has you covered for those nights when you need " +
"to get out, or even if you’re just headed to work."
),
new Product(
name: "Backpack",
imageAsset: "products/backpack",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"travel", "fashion"},
price: 25.00f,
vendor: _peter,
description:
"This backpack by Bags ‘n’ stuff can hold just about anything: a laptop, " +
"a pen, a protractor, notebooks, small animals, plugs for your devices, " +
"sunglasses, gym clothes, shoes, gloves, two kittens, and even lunch!"
),
new Product(
name: "Charcoal Straw Hat",
imageAsset: "products/hat",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"travel", "fashion", "latest"},
price: 25.00f,
vendor: _ali,
description:
"This is the helmet for those warm summer days on the road. " +
"Jetset approved, these hats have been rigorously tested. Keep that face " +
"protected from the sun."
),
new Product(
name: "Ginger Scarf",
imageAsset: "products/scarf",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"latest", "fashion"},
price: 17.00f,
vendor: _peter,
description:
"Leave the tunnel and the rain is fallin amazing things happen when you wait"
),
new Product(
name: "Blush Sweats",
imageAsset: "products/sweats",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"travel", "fashion", "latest"},
price: 25.00f,
vendor: _stella,
description:
"Leave the tunnel and the rain is fallin amazing things happen when you wait"
),
new Product(
name: "Mint Jumper",
imageAsset: "products/jumper",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"travel", "fashion", "beauty"},
price: 25.00f,
vendor: _peter,
description:
"Leave the tunnel and the rain is fallin amazing things happen when you wait"
),
new Product(
name: "Ochre Shirt",
imageAsset: "products/shirt",
imageAssetPackage: _kGalleryAssetsPackage,
categories: new List<string> {"fashion", "latest"},
price: 120.00f,
vendor: _stella,
description:
"Leave the tunnel and the rain is fallin amazing things happen when you wait"
)
};
public static List<Product> allProducts() {
D.assert(_allProducts.All((Product product) => product.isValid()));
return new List<Product>(_allProducts);
}
}
}

3
Samples/UIWidgetsGallery/demo/shrine/shrine_data.cs.meta


fileFormatVersion: 2
guid: f29bb63cff3148cbb3ef3be8196d5709
timeCreated: 1553240837

410
Samples/UIWidgetsGallery/demo/shrine/shrine_home.cs


using System.Collections.Generic;
using System.Linq;
using com.unity.uiwidgets.Runtime.rendering;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using Constants = Unity.UIWidgets.material.Constants;
using Image = Unity.UIWidgets.widgets.Image;
namespace UIWidgetsGallery.gallery {
class ShrineHomeUtils {
public const float unitSize = Constants.kToolbarHeight;
public static readonly List<Product> _products = new List<Product>(ShrineData.allProducts());
public static readonly Dictionary<Product, Order> _shoppingCart = new Dictionary<Product, Order>();
public const int _childrenPerBlock = 8;
public const int _rowsPerBlock = 5;
public static int _minIndexInRow(int rowIndex) {
int blockIndex = rowIndex / _rowsPerBlock;
return new List<int> {0, 2, 4, 6, 7}[rowIndex % _rowsPerBlock] + blockIndex * _childrenPerBlock;
}
public static int _maxIndexInRow(int rowIndex) {
int blockIndex = rowIndex / _rowsPerBlock;
return new List<int> {1, 3, 5, 6, 7}[rowIndex % _rowsPerBlock] + blockIndex * _childrenPerBlock;
}
public static int _rowAtIndex(int index) {
int blockCount = index / _childrenPerBlock;
return new List<int> {0, 0, 1, 1, 2, 2, 3, 4}[index - blockCount * _childrenPerBlock] +
blockCount * _rowsPerBlock;
}
public static int _columnAtIndex(int index) {
return new List<int> {0, 1, 0, 1, 0, 1, 0, 0}[index % _childrenPerBlock];
}
public static int _columnSpanAtIndex(int index) {
return new List<int> {1, 1, 1, 1, 1, 1, 2, 2}[index % _childrenPerBlock];
}
}
class _ShrineGridLayout : SliverGridLayout {
public _ShrineGridLayout(
float? rowStride = null,
float? columnStride = null,
float? tileHeight = null,
float? tileWidth = null
) {
this.rowStride = rowStride;
this.columnStride = columnStride;
this.tileHeight = tileHeight;
this.tileWidth = tileWidth;
}
public readonly float? rowStride;
public readonly float? columnStride;
public readonly float? tileHeight;
public readonly float? tileWidth;
public override int getMinChildIndexForScrollOffset(float scrollOffset) {
return ShrineHomeUtils._minIndexInRow((int) (scrollOffset / this.rowStride));
}
public override int getMaxChildIndexForScrollOffset(float scrollOffset) {
return ShrineHomeUtils._maxIndexInRow((int) (scrollOffset / this.rowStride));
}
public override SliverGridGeometry getGeometryForChildIndex(int index) {
int row = ShrineHomeUtils._rowAtIndex(index);
int column = ShrineHomeUtils._columnAtIndex(index);
int columnSpan = ShrineHomeUtils._columnSpanAtIndex(index);
return new SliverGridGeometry(
scrollOffset: row * this.rowStride,
crossAxisOffset: column * this.columnStride,
mainAxisExtent: this.tileHeight,
crossAxisExtent: this.tileWidth + (columnSpan - 1) * this.columnStride
);
}
public override float computeMaxScrollOffset(int childCount) {
if (childCount == 0) {
return 0.0f;
}
int rowCount = ShrineHomeUtils._rowAtIndex(childCount - 1) + 1;
float? rowSpacing = this.rowStride - this.tileHeight;
return (this.rowStride * rowCount - rowSpacing) ?? 0.0f;
}
}
class _ShrineGridDelegate : SliverGridDelegate {
const float _spacing = 8.0f;
public override SliverGridLayout getLayout(SliverConstraints constraints) {
float tileWidth = (constraints.crossAxisExtent - _spacing) / 2.0f;
const float tileHeight = 40.0f + 144.0f + 40.0f;
return new _ShrineGridLayout(
tileWidth: tileWidth,
tileHeight: tileHeight,
rowStride: tileHeight + _spacing,
columnStride: tileWidth + _spacing
);
}
public override bool shouldRelayout(SliverGridDelegate oldDelegate) {
return false;
}
}
class _VendorItem : StatelessWidget {
public _VendorItem(Key key = null, Vendor vendor = null) : base(key: key) {
D.assert(vendor != null);
this.vendor = vendor;
}
public readonly Vendor vendor;
public override Widget build(BuildContext context) {
return new SizedBox(
height: 24.0f,
child: new Row(
children: new List<Widget> {
new SizedBox(
width: 24.0f,
child: new ClipRRect(
borderRadius: BorderRadius.circular(12.0f),
child: Image.asset(
this.vendor.avatarAsset,
fit: BoxFit.cover
)
)
),
new SizedBox(width: 8.0f),
new Expanded(
child: new Text(this.vendor.name, style: ShrineTheme.of(context).vendorItemStyle)
)
}
)
);
}
}
abstract class _PriceItem : StatelessWidget {
public _PriceItem(Key key = null, Product product = null)
: base(key: key) {
D.assert(product != null);
this.product = product;
}
public readonly Product product;
public Widget buildItem(BuildContext context, TextStyle style, EdgeInsets padding) {
BoxDecoration decoration = null;
if (ShrineHomeUtils._shoppingCart.getOrDefault(this.product) != null) {
decoration = new BoxDecoration(color: ShrineTheme.of(context).priceHighlightColor);
}
return new Container(
padding: padding,
decoration: decoration,
child: new Text(this.product.priceString, style: style)
);
}
}
class _ProductPriceItem : _PriceItem {
public _ProductPriceItem(Key key = null, Product product = null) : base(key: key, product: product) {
}
public override Widget build(BuildContext context) {
return this.buildItem(
context,
ShrineTheme.of(context).priceStyle,
EdgeInsets.symmetric(horizontal: 16.0f, vertical: 8.0f)
);
}
}
class _FeaturePriceItem : _PriceItem {
public _FeaturePriceItem(Key key = null, Product product = null) : base(key: key, product: product) {
}
public override Widget build(BuildContext context) {
return this.buildItem(
context,
ShrineTheme.of(context).featurePriceStyle,
EdgeInsets.symmetric(horizontal: 24.0f, vertical: 16.0f)
);
}
}
class _HeadingLayout : MultiChildLayoutDelegate {
public _HeadingLayout() {
}
public const string price = "price";
public const string image = "image";
public const string title = "title";
public const string description = "description";
public const string vendor = "vendor";
public override void performLayout(Size size) {
Size priceSize = this.layoutChild(price, BoxConstraints.loose(size));
this.positionChild(price, new Offset(size.width - priceSize.width, 0.0f));
float halfWidth = size.width / 2.0f;
float halfHeight = size.height / 2.0f;
const float halfUnit = ShrineHomeUtils.unitSize / 2.0f;
const float margin = 16.0f;
Size imageSize = this.layoutChild(image, BoxConstraints.loose(size));
float imageX = imageSize.width < halfWidth - halfUnit
? halfWidth / 2.0f - imageSize.width / 2.0f - halfUnit
: halfWidth - imageSize.width;
this.positionChild(image, new Offset(imageX, halfHeight - imageSize.height / 2.0f));
float maxTitleWidth = halfWidth + ShrineHomeUtils.unitSize - margin;
BoxConstraints titleBoxConstraints = new BoxConstraints(maxWidth: maxTitleWidth);
Size titleSize = this.layoutChild(title, titleBoxConstraints);
float titleX = halfWidth - ShrineHomeUtils.unitSize;
float titleY = halfHeight - titleSize.height;
this.positionChild(title, new Offset(titleX, titleY));
Size descriptionSize = this.layoutChild(description, titleBoxConstraints);
float descriptionY = titleY + titleSize.height + margin;
this.positionChild(description, new Offset(titleX, descriptionY));
this.layoutChild(vendor, titleBoxConstraints);
float vendorY = descriptionY + descriptionSize.height + margin;
this.positionChild(vendor, new Offset(titleX, vendorY));
}
public override bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) {
return false;
}
}
class _HeadingShrineHome : StatelessWidget {
public _HeadingShrineHome(Key key = null, Product product = null) : base(key: key) {
D.assert(product != null);
D.assert(product.featureTitle != null);
D.assert(product.featureDescription != null);
this.product = product;
}
public readonly Product product;
public override Widget build(BuildContext context) {
Size screenSize = MediaQuery.of(context).size;
ShrineTheme theme = ShrineTheme.of(context);
return new SizedBox(
height: screenSize.width > screenSize.height
? (screenSize.height - Constants.kToolbarHeight) * 0.85f
: (screenSize.height - Constants.kToolbarHeight) * 0.70f,
child: new Container(
decoration: new BoxDecoration(
color: theme.cardBackgroundColor,
border: new Border(bottom: new BorderSide(color: theme.dividerColor))
),
child: new CustomMultiChildLayout(
layoutDelegate: new _HeadingLayout(),
children: new List<Widget> {
new LayoutId(
id: _HeadingLayout.price,
child: new _FeaturePriceItem(product: this.product)
),
new LayoutId(
id: _HeadingLayout.image,
child: Image.asset(
this.product.imageAsset,
fit: BoxFit.cover
)
),
new LayoutId(
id: _HeadingLayout.title,
child: new Text(this.product.featureTitle, style: theme.featureTitleStyle)
),
new LayoutId(
id: _HeadingLayout.description,
child: new Text(this.product.featureDescription, style: theme.featureStyle)
),
new LayoutId(
id: _HeadingLayout.vendor,
child: new _VendorItem(vendor: this.product.vendor)
)
}
)
)
);
}
}
class _ProductItem : StatelessWidget {
public _ProductItem(Key key = null, Product product = null, VoidCallback onPressed = null) : base(key: key) {
D.assert(product != null);
this.product = product;
this.onPressed = onPressed;
}
public readonly Product product;
public readonly VoidCallback onPressed;
public override Widget build(BuildContext context) {
return new Card(
child: new Stack(
children: new List<Widget> {
new Column(
children: new List<Widget> {
new Align(
alignment: Alignment.centerRight,
child: new _ProductPriceItem(product: this.product)
),
new Container(
width: 144.0f,
height: 144.0f,
padding: EdgeInsets.symmetric(horizontal: 8.0f),
child:new Hero(
tag: this.product.tag,
child: Image.asset(this.product.imageAsset,
fit: BoxFit.contain
)
)
),
new Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0f),
child: new _VendorItem(vendor: this.product.vendor)
)
}
),
new Material(
type: MaterialType.transparency,
child: new InkWell(onTap: this.onPressed == null
? (GestureTapCallback) null
: () => { this.onPressed(); })
)
}
)
);
}
}
public class ShrineHome : StatefulWidget {
public override State createState() {
return new _ShrineHomeState();
}
}
class _ShrineHomeState : State<ShrineHome> {
public _ShrineHomeState() {
}
readonly GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>.key(debugLabel: "Shrine Home");
static readonly _ShrineGridDelegate gridDelegate = new _ShrineGridDelegate();
void _showOrderPage(Product product) {
Order order = ShrineHomeUtils._shoppingCart.getOrDefault(product) ?? new Order(product: product);
Navigator.push(this.context, new ShrineOrderRoute(
order: order,
builder: (BuildContext context) => {
return new OrderPage(
order: order,
products: ShrineHomeUtils._products,
shoppingCart: ShrineHomeUtils._shoppingCart
);
}
)).Then(completedOrder => {
D.assert((completedOrder as Order).product != null);
if ((completedOrder as Order).quantity == 0) {
ShrineHomeUtils._shoppingCart.Remove((completedOrder as Order).product);
}
});
}
public override Widget build(BuildContext context) {
Product featured = ShrineHomeUtils._products.First((Product product) => product.featureDescription != null);
return new ShrinePage(
scaffoldKey: this._scaffoldKey,
products: ShrineHomeUtils._products,
shoppingCart: ShrineHomeUtils._shoppingCart,
body: new CustomScrollView(
slivers: new List<Widget> {
new SliverToBoxAdapter(child: new _HeadingShrineHome(product: featured)),
new SliverSafeArea(
top: false,
minimum: EdgeInsets.all(16.0f),
sliver: new SliverGrid(
gridDelegate: gridDelegate,
layoutDelegate: new SliverChildListDelegate(
ShrineHomeUtils._products.Select<Product, Widget>((Product product) => {
return new _ProductItem(
product: product,
onPressed: () => { this._showOrderPage(product); }
);
}).ToList()
)
)
)
}
)
);
}
}
}

11
Samples/UIWidgetsGallery/demo/shrine/shrine_home.cs.meta


fileFormatVersion: 2
guid: ec9f3366e13014909b0287e1305641e1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

3
Samples/UIWidgetsGallery/demo/shrine/shrine_order.cs.meta


fileFormatVersion: 2
guid: b0720924e140457dbc8626755c82f3ba
timeCreated: 1553244438

156
Samples/UIWidgetsGallery/demo/shrine/shrine_page.cs


using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.service;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery.gallery {
public enum ShrineAction {
sortByPrice,
sortByProduct,
emptyCart
}
public class ShrinePage : StatefulWidget {
public ShrinePage(
Key key = null,
GlobalKey<ScaffoldState> scaffoldKey = null,
Widget body = null,
Widget floatingActionButton = null,
List<Product> products = null,
Dictionary<Product, Order> shoppingCart = null
) : base(key: key) {
D.assert(body != null);
D.assert(scaffoldKey != null);
this.scaffoldKey = scaffoldKey;
this.body = body;
this.floatingActionButton = floatingActionButton;
this.products = products;
this.shoppingCart = shoppingCart;
}
public readonly GlobalKey<ScaffoldState> scaffoldKey;
public readonly Widget body;
public readonly Widget floatingActionButton;
public readonly List<Product> products;
public readonly Dictionary<Product, Order> shoppingCart;
public override State createState() {
return new ShrinePageState();
}
}
public class ShrinePageState : State<ShrinePage> {
float _appBarElevation = 0.0f;
bool _handleScrollNotification(ScrollNotification notification) {
float elevation = notification.metrics.extentBefore() <= 0.0f ? 0.0f : 1.0f;
if (elevation != this._appBarElevation) {
this.setState(() => { this._appBarElevation = elevation; });
}
return false;
}
void _showShoppingCart() {
BottomSheetUtils.showModalBottomSheet<object>(context: this.context, builder: (BuildContext context) => {
if (this.widget.shoppingCart.isEmpty()) {
return new Padding(
padding: EdgeInsets.all(24.0f),
child: new Text("The shopping cart is empty")
);
}
return new ListView(
padding: Constants.kMaterialListPadding,
children: this.widget.shoppingCart.Values.Select<Order, Widget>((Order order) => {
return new ListTile(
title: new Text(order.product.name),
leading: new Text($"{order.quantity}"),
subtitle: new Text(order.product.vendor.name)
);
}).ToList()
);
});
}
void _sortByPrice() {
this.widget.products.Sort((Product a, Product b) => a.price?.CompareTo(b.price) ?? (b == null ? 0 : -1));
}
void _sortByProduct() {
this.widget.products.Sort((Product a, Product b) => a.name.CompareTo(b.name));
}
void _emptyCart() {
this.widget.shoppingCart.Clear();
this.widget.scaffoldKey.currentState.showSnackBar(
new SnackBar(content: new Text("Shopping cart is empty")));
}
public override Widget build(BuildContext context) {
ShrineTheme theme = ShrineTheme.of(context);
return new Scaffold(
key: this.widget.scaffoldKey,
appBar: new AppBar(
elevation: this._appBarElevation,
backgroundColor: theme.appBarBackgroundColor,
iconTheme: Theme.of(context).iconTheme,
brightness: Brightness.light,
flexibleSpace: new Container(
decoration: new BoxDecoration(
border: new Border(
bottom: new BorderSide(color: theme.dividerColor)
)
)
),
title: new Text("SHRINE", style: ShrineTheme.of(context).appBarTitleStyle),
centerTitle: true,
actions: new List<Widget> {
new IconButton(
icon: new Icon(Icons.shopping_cart),
tooltip: "Shopping cart",
onPressed: this._showShoppingCart
),
new PopupMenuButton<ShrineAction>(
itemBuilder: (BuildContext _) => new List<PopupMenuEntry<ShrineAction>> {
new PopupMenuItem<ShrineAction>(
value: ShrineAction.sortByPrice,
child: new Text("Sort by price")
),
new PopupMenuItem<ShrineAction>(
value: ShrineAction.sortByProduct,
child: new Text("Sort by product")
),
new PopupMenuItem<ShrineAction>(
value: ShrineAction.emptyCart,
child: new Text("Empty shopping cart")
)
},
onSelected: (ShrineAction action) => {
switch (action) {
case ShrineAction.sortByPrice:
this.setState(this._sortByPrice);
break;
case ShrineAction.sortByProduct:
this.setState(this._sortByProduct);
break;
case ShrineAction.emptyCart:
this.setState(this._emptyCart);
break;
}
}
)
}
),
floatingActionButton: this.widget.floatingActionButton,
body: new NotificationListener<ScrollNotification>(
onNotification: this._handleScrollNotification,
child: this.widget.body
)
);
}
}
}

3
Samples/UIWidgetsGallery/demo/shrine/shrine_page.cs.meta


fileFormatVersion: 2
guid: 81f9ca5ad3e848f4889059abd3037d17
timeCreated: 1553246179

98
Samples/UIWidgetsGallery/demo/shrine/shrine_theme.cs


using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace UIWidgetsGallery.gallery {
public class ShrineStyle : TextStyle {
public ShrineStyle(bool inherit, Color color, float fontSize, FontWeight fontWeight, TextBaseline textBaseline,
string fontFamily = null
) : base(inherit: inherit, color: color, fontSize: fontSize, fontWeight: fontWeight, fontFamily: fontFamily,
textBaseline: textBaseline) {
}
public static ShrineStyle roboto(float size, FontWeight weight, Color color) {
return new ShrineStyle(inherit: false, color: color, fontSize: size, fontWeight: weight,
textBaseline: TextBaseline.alphabetic);
}
public static ShrineStyle abrilFatface(float size, FontWeight weight, Color color) {
return new ShrineStyle(inherit: false, color: color, fontFamily: "AbrilFatface", fontSize: size,
fontWeight: weight, textBaseline: TextBaseline.alphabetic);
}
}
public class ShrineThemeUtils {
public static TextStyle robotoRegular12(Color color) {
return ShrineStyle.roboto(12.0f, FontWeight.w400, color);
}
public static TextStyle robotoLight12(Color color) {
return ShrineStyle.roboto(12.0f, FontWeight.w400, color);
}
public static TextStyle robotoRegular14(Color color) {
return ShrineStyle.roboto(14.0f, FontWeight.w400, color);
}
public static TextStyle robotoMedium14(Color color) {
return ShrineStyle.roboto(14.0f, FontWeight.w700, color);
}
public static TextStyle robotoLight14(Color color) {
return ShrineStyle.roboto(14.0f, FontWeight.w400, color);
}
public static TextStyle robotoRegular16(Color color) {
return ShrineStyle.roboto(16.0f, FontWeight.w400, color);
}
public static TextStyle robotoRegular20(Color color) {
return ShrineStyle.roboto(20.0f, FontWeight.w400, color);
}
public static TextStyle abrilFatfaceRegular24(Color color) {
return ShrineStyle.abrilFatface(24.0f, FontWeight.w400, color);
}
public static TextStyle abrilFatfaceRegular34(Color color) {
return ShrineStyle.abrilFatface(34.0f, FontWeight.w400, color);
}
}
public class ShrineTheme : InheritedWidget {
public ShrineTheme(Key key = null, Widget child = null)
: base(key: key, child: child) {
D.assert(child != null);
}
public readonly Color cardBackgroundColor = Colors.white;
public readonly Color appBarBackgroundColor = Colors.white;
public readonly Color dividerColor = new Color(0xFFD9D9D9);
public readonly Color priceHighlightColor = new Color(0xFFFFE0E0);
public readonly TextStyle appBarTitleStyle = ShrineThemeUtils.robotoRegular20(Colors.black87);
public readonly TextStyle vendorItemStyle = ShrineThemeUtils.robotoRegular12(new Color(0xFF81959D));
public readonly TextStyle priceStyle = ShrineThemeUtils.robotoRegular14(Colors.black87);
public readonly TextStyle featureTitleStyle =
ShrineThemeUtils.abrilFatfaceRegular34(new Color(0xFF0A3142));
public readonly TextStyle featurePriceStyle = ShrineThemeUtils.robotoRegular16(Colors.black87);
public readonly TextStyle featureStyle = ShrineThemeUtils.robotoLight14(Colors.black54);
public readonly TextStyle orderTitleStyle = ShrineThemeUtils.abrilFatfaceRegular24(Colors.black87);
public readonly TextStyle orderStyle = ShrineThemeUtils.robotoLight14(Colors.black54);
public readonly TextStyle vendorTitleStyle = ShrineThemeUtils.robotoMedium14(Colors.black87);
public readonly TextStyle vendorStyle = ShrineThemeUtils.robotoLight14(Colors.black54);
public readonly TextStyle quantityMenuStyle = ShrineThemeUtils.robotoLight14(Colors.black54);
public static ShrineTheme of(BuildContext context) {
return (ShrineTheme) context.inheritFromWidgetOfExactType(typeof(ShrineTheme));
}
public override bool updateShouldNotify(InheritedWidget oldWidget) {
return false;
}
}
}

3
Samples/UIWidgetsGallery/demo/shrine/shrine_theme.cs.meta


fileFormatVersion: 2
guid: 468619e1fd4f4437ba3f0929e269aab4
timeCreated: 1553241961

153
Samples/UIWidgetsGallery/demo/shrine/shrine_types.cs


using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
namespace UIWidgetsGallery.gallery {
public class Vendor {
public Vendor(
string name,
string description,
string avatarAsset,
string avatarAssetPackage
) {
this.name = name;
this.description = description;
this.avatarAsset = avatarAsset;
this.avatarAssetPackage = avatarAssetPackage;
}
public readonly string name;
public readonly string description;
public readonly string avatarAsset;
public readonly string avatarAssetPackage;
public bool isValid() {
return this.name != null && this.description != null && this.avatarAsset != null;
}
public override string ToString() {
return "Vendor($name)";
}
}
public class Product {
public Product(
string name = null,
string description = null,
string featureTitle = null,
string featureDescription = null,
string imageAsset = null,
string imageAssetPackage = null,
List<string> categories = null,
float? price = null,
Vendor vendor = null
) {
this.name = name;
this.description = description;
this.featureTitle = featureTitle;
this.featureDescription = featureDescription;
this.imageAsset = imageAsset;
this.imageAssetPackage = imageAssetPackage;
this.categories = categories;
this.price = price;
this.vendor = vendor;
}
public readonly string name;
public readonly string description;
public readonly string featureTitle;
public readonly string featureDescription;
public readonly string imageAsset;
public readonly string imageAssetPackage;
public readonly List<string> categories;
public readonly float? price;
public readonly Vendor vendor;
public string tag {
get { return this.name; }
}
public string priceString {
get { return $"${(this.price ?? 0.0f).floor()}"; }
}
public bool isValid() {
return this.name != null && this.description != null && this.imageAsset != null &&
this.categories != null && this.categories.isNotEmpty() && this.price != null &&
this.vendor.isValid();
}
public override string ToString() {
return $"Product({this.name})";
}
}
public class Order {
public Order(Product product, int quantity = 1, bool inCart = false) {
D.assert(product != null);
D.assert(quantity >= 0);
this.product = product;
this.quantity = quantity;
this.inCart = inCart;
}
public readonly Product product;
public readonly int quantity;
public readonly bool inCart;
public Order copyWith(Product product = null, int? quantity = null, bool? inCart = null) {
return new Order(
product: product ?? this.product,
quantity: quantity ?? this.quantity,
inCart: inCart ?? this.inCart
);
}
public static bool operator ==(Order left, Order right) {
if (left is null && right is null) return true;
if (left is null || right is null) return false;
return left.Equals(right);
}
public static bool operator !=(Order left, Order right) {
if (left is null && right is null) return false;
if (left is null || right is null) return true;
return !left.Equals(right);
}
public bool Equals(Order other) {
return this.product == other.product &&
this.quantity == other.quantity &&
this.inCart == other.inCart;
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {
return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != this.GetType()) {
return false;
}
return this.Equals((Order) obj);
}
public override int GetHashCode() {
unchecked {
var hashCode = (this.product != null ? this.product.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ this.quantity.GetHashCode();
hashCode = (hashCode * 397) ^ this.inCart.GetHashCode();
return hashCode;
}
}
public override string ToString() {
return $"Order({this.product}, quantity={this.quantity}, inCart={this.inCart})";
}
}
}

3
Samples/UIWidgetsGallery/demo/shrine/shrine_types.cs.meta


fileFormatVersion: 2
guid: 924c1662fb504687af28a4f05901a20a
timeCreated: 1553240139

341
Samples/UIWidgetsGallery/demo/shrine/shrine_order.cs


using System.Collections.Generic;
using System.Linq;
using com.unity.uiwidgets.Runtime.rendering;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using Image = Unity.UIWidgets.widgets.Image;
namespace UIWidgetsGallery.gallery {
class _ProductItemShrineOrder : StatelessWidget {
public _ProductItemShrineOrder(
Key key = null,
Product product = null,
int? quantity = null,
ValueChanged<int> onChanged = null
) : base(key: key) {
D.assert(product != null);
D.assert(quantity != null);
D.assert(onChanged != null);
this.product = product;
this.quantity = quantity;
this.onChanged = onChanged;
}
public readonly Product product;
public readonly int? quantity;
public readonly ValueChanged<int> onChanged;
public override Widget build(BuildContext context) {
ShrineTheme theme = ShrineTheme.of(context);
return new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: new List<Widget> {
new Text(this.product.name, style: theme.featureTitleStyle),
new SizedBox(height: 24.0f),
new Text(this.product.description, style: theme.featureStyle),
new SizedBox(height: 16.0f),
new Padding(
padding: EdgeInsets.only(top: 8.0f, bottom: 8.0f, right: 88.0f),
child: new DropdownButtonHideUnderline(
child: new Container(
decoration: new BoxDecoration(
border: Border.all(
color: new Color(0xFFD9D9D9)
)
),
child: new DropdownButton<string>(
items: new List<int> {0, 1, 2, 3, 4, 5}.Select<int, DropdownMenuItem<string>>(
(int value) => {
return new DropdownMenuItem<string>(
value: $"{value}",
child: new Padding(
padding: EdgeInsets.only(left: 8.0f),
child: new Text($"Quantity {value}", style: theme.quantityMenuStyle)
)
);
}).ToList(),
value: $"{this.quantity}",
onChanged: (value) => { this.onChanged(int.Parse(value)); })
)
)
)
}
);
}
}
class _VendorItemShrineOrder : StatelessWidget {
public _VendorItemShrineOrder(Key key = null, Vendor vendor = null)
: base(key: key) {
D.assert(vendor != null);
this.vendor = vendor;
}
public readonly Vendor vendor;
public override Widget build(BuildContext context) {
ShrineTheme theme = ShrineTheme.of(context);
return new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: new List<Widget> {
new SizedBox(
height: 24.0f,
child: new Align(
alignment: Alignment.bottomLeft,
child: new Text(this.vendor.name, style: theme.vendorTitleStyle)
)
),
new SizedBox(height: 16.0f),
new Text(this.vendor.description, style: theme.vendorStyle)
}
);
}
}
class _HeadingLayoutShrineOrder : MultiChildLayoutDelegate {
public _HeadingLayoutShrineOrder() {
}
public const string image = "image";
public const string icon = "icon";
public const string product = "product";
public const string vendor = "vendor";
public override void performLayout(Size size) {
const float margin = 56.0f;
bool landscape = size.width > size.height;
float imageWidth = (landscape ? size.width / 2.0f : size.width) - margin * 2.0f;
BoxConstraints imageConstraints = new BoxConstraints(maxHeight: 224.0f, maxWidth: imageWidth);
Size imageSize = this.layoutChild(image, imageConstraints);
const float imageY = 0.0f;
this.positionChild(image, new Offset(margin, imageY));
float productWidth = landscape ? size.width / 2.0f : size.width - margin;
BoxConstraints productConstraints = new BoxConstraints(maxWidth: productWidth);
Size productSize = this.layoutChild(product, productConstraints);
float productX = landscape ? size.width / 2.0f : margin;
float productY = landscape ? 0.0f : imageY + imageSize.height + 16.0f;
this.positionChild(product, new Offset(productX, productY));
Size iconSize = this.layoutChild(icon, BoxConstraints.loose(size));
this.positionChild(icon, new Offset(productX - iconSize.width - 16.0f, productY + 8.0f));
float vendorWidth = landscape ? size.width - margin : productWidth;
this.layoutChild(vendor, new BoxConstraints(maxWidth: vendorWidth));
float vendorX = landscape ? margin : productX;
float vendorY = productY + productSize.height + 16.0f;
this.positionChild(vendor, new Offset(vendorX, vendorY));
}
public override bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) {
return true;
}
}
class _HeadingShrineOrder : StatelessWidget {
public _HeadingShrineOrder(
Key key = null,
Product product = null,
int? quantity = null,
ValueChanged<int> quantityChanged = null
) : base(key: key) {
D.assert(product != null);
D.assert(quantity != null && quantity >= 0 && quantity <= 5);
this.product = product;
this.quantity = quantity;
this.quantityChanged = quantityChanged;
}
public readonly Product product;
public readonly int? quantity;
public readonly ValueChanged<int> quantityChanged;
public override Widget build(BuildContext context) {
Size screenSize = MediaQuery.of(context).size;
return new SizedBox(
height: (screenSize.height - Constants.kToolbarHeight) * 1.35f,
child: new Material(
type: MaterialType.card,
elevation: 0.0f,
child: new Padding(
padding: EdgeInsets.only(left: 16.0f, top: 18.0f, right: 16.0f, bottom: 24.0f),
child: new CustomMultiChildLayout(
layoutDelegate: new _HeadingLayoutShrineOrder(),
children: new List<Widget> {
new LayoutId(
id: _HeadingLayoutShrineOrder.image,
child: new Hero(
tag: this.product.tag,
child: Image.asset(this.product.imageAsset,
fit: BoxFit.contain,
alignment: Alignment.center
)
)
),
new LayoutId(
id: _HeadingLayoutShrineOrder.icon,
child: new Icon(
Icons.info_outline,
size: 24.0f,
color: new Color(0xFFFFE0E0)
)
),
new LayoutId(
id: _HeadingLayoutShrineOrder.product,
child: new _ProductItemShrineOrder(
product: this.product,
quantity: this.quantity,
onChanged: this.quantityChanged
)
),
new LayoutId(
id: _HeadingLayoutShrineOrder.vendor,
child: new _VendorItemShrineOrder(vendor: this.product.vendor)
)
}
)
)
)
);
}
}
public class OrderPage : StatefulWidget {
public OrderPage(
Key key = null,
Order order = null,
List<Product> products = null,
Dictionary<Product, Order> shoppingCart = null
) : base(key: key) {
D.assert(order != null);
D.assert(products != null && products.isNotEmpty());
D.assert(shoppingCart != null);
this.order = order;
this.products = products;
this.shoppingCart = shoppingCart;
}
public readonly Order order;
public readonly List<Product> products;
public readonly Dictionary<Product, Order> shoppingCart;
public override State createState() {
return new _OrderPageState();
}
}
class _OrderPageState : State<OrderPage> {
GlobalKey<ScaffoldState> scaffoldKey;
public override void initState() {
base.initState();
this.scaffoldKey = GlobalKey<ScaffoldState>.key(debugLabel: $"Shrine Order {this.widget.order}");
}
public Order currentOrder {
get { return ShrineOrderRoute.of(this.context).order; }
set { ShrineOrderRoute.of(this.context).order = value; }
}
void updateOrder(int? quantity = null, bool? inCart = null) {
Order newOrder = this.currentOrder.copyWith(quantity: quantity, inCart: inCart);
if (this.currentOrder != newOrder) {
this.setState(() => {
this.widget.shoppingCart[newOrder.product] = newOrder;
this.currentOrder = newOrder;
});
}
}
void showSnackBarMessage(string message) {
this.scaffoldKey.currentState.showSnackBar(new SnackBar(content: new Text(message)));
}
public override Widget build(BuildContext context) {
return new ShrinePage(
scaffoldKey: this.scaffoldKey,
products: this.widget.products,
shoppingCart: this.widget.shoppingCart,
floatingActionButton: new FloatingActionButton(
onPressed: () => {
this.updateOrder(inCart: true);
int n = this.currentOrder.quantity;
string item = this.currentOrder.product.name;
string message = n == 1 ? $"is one {item} item" : $"are {n} {item} items";
this.showSnackBarMessage(
$"There {message} in the shopping cart."
);
},
backgroundColor: new Color(0xFF16F0F0),
tooltip: "Add to cart",
child: new Icon(
Icons.add_shopping_cart,
color: Colors.black
)
),
body: new CustomScrollView(
slivers: new List<Widget> {
new SliverToBoxAdapter(
child: new _HeadingShrineOrder(
product: this.widget.order.product,
quantity: this.currentOrder.quantity,
quantityChanged: (int value) => { this.updateOrder(quantity: value); }
)
),
new SliverSafeArea(
top: false,
minimum: EdgeInsets.fromLTRB(8.0f, 32.0f, 8.0f, 8.0f),
sliver: new SliverGrid(
gridDelegate: new SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 248.0f,
mainAxisSpacing: 8.0f,
crossAxisSpacing: 8.0f
),
layoutDelegate: new SliverChildListDelegate(
this.widget.products
.FindAll((Product product) => product != this.widget.order.product)
.Select<Product, Widget>((Product product) => {
return new Card(
elevation: 1.0f,
child: Image.asset(
product.imageAsset,
fit: BoxFit.contain
)
);
}).ToList()
)
)
)
}
)
);
}
}
public class ShrineOrderRoute : ShrinePageRoute<Order> {
public ShrineOrderRoute(
Order order = null,
WidgetBuilder builder = null,
RouteSettings settings = null
) : base(builder: builder, settings: settings) {
D.assert(order != null);
this.order = order;
}
public Order order;
public new Order currentResult {
get { return this.order; }
}
public new static ShrineOrderRoute of(BuildContext context) {
return (ShrineOrderRoute) ModalRoute.of(context);
}
}
}

1001
Tests/Resources/people/ali_landscape.png
文件差异内容过多而无法显示
查看文件

88
Tests/Resources/people/ali_landscape.png.meta


fileFormatVersion: 2
guid: 0842a5ed1d4784702990d69f6c5d348d
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: -1
wrapV: -1
wrapW: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

8
Tests/Resources/people/square.meta


fileFormatVersion: 2
guid: 321c11a98ef1c452ba9bf4fad8ac67fe
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

457
Tests/Resources/people/square/ali.png

之前 之后
宽度: 300  |  高度: 300  |  大小: 127 KiB

88
Tests/Resources/people/square/ali.png.meta


fileFormatVersion: 2
guid: f644f877472c942209cc4ff5beaf452a
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: -1
wrapV: -1
wrapW: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

509
Tests/Resources/people/square/peter.png

之前 之后
宽度: 300  |  高度: 300  |  大小: 161 KiB

88
Tests/Resources/people/square/peter.png.meta


fileFormatVersion: 2
guid: 739817fca86d74ffda90f9a891db4c99
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: -1
wrapV: -1
wrapW: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

698
Tests/Resources/people/square/sandra.png

之前 之后
宽度: 300  |  高度: 300  |  大小: 165 KiB

88
Tests/Resources/people/square/sandra.png.meta


fileFormatVersion: 2
guid: 61c32ad851b0c4c90a05f5857c8bf810
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: -1
wrapV: -1
wrapW: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

393
Tests/Resources/people/square/stella.png

之前 之后
宽度: 300  |  高度: 300  |  大小: 128 KiB

88
Tests/Resources/people/square/stella.png.meta


fileFormatVersion: 2
guid: 89e334df4c00c47258db57d508fcb3d0
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: -1
wrapV: -1
wrapW: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

574
Tests/Resources/people/square/trevor.png

之前 之后
宽度: 300  |  高度: 300  |  大小: 156 KiB

88
Tests/Resources/people/square/trevor.png.meta


fileFormatVersion: 2
guid: 88e5367b95d2f462693ad5524593ab14
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: -1
wrapV: -1
wrapW: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

98
Runtime/service/performance_utils.cs


using System.Diagnostics;
using Unity.UIWidgets.foundation;
namespace Unity.UIWidgets.service {
public class PerformanceUtils {
public static PerformanceUtils instance {
get {
if (_instance != null) {
return _instance;
}
_instance = new PerformanceUtils();
_instance._setup();
return _instance;
}
}
static PerformanceUtils _instance;
const int FrameBufferSize = 200;
float[] _frames;
int _curFrameId;
Stopwatch _stopwatch;
float deltaTime = 0.0f;
bool _enabled;
void _setup() {
this._frames = new float[FrameBufferSize];
this._curFrameId = -1;
this._enabled = false;
}
void _ensureStopWatch() {
if (this._stopwatch == null) {
this._stopwatch = new Stopwatch();
}
}
public void updateDeltaTime(float unscaledDeltaTime) {
this.deltaTime += (unscaledDeltaTime - this.deltaTime) * 0.1f;
}
public float getFPS() {
return 1.0f / this.deltaTime;
}
public void startProfile() {
if (!this._enabled) {
return;
}
this._ensureStopWatch();
if (this._stopwatch.IsRunning) {
D.assert(false, "Try to start the stopwatch when it is already running");
return;
}
this._stopwatch.Start();
}
public void endProfile() {
if (!this._enabled || this._stopwatch == null) {
return;
}
if (!this._stopwatch.IsRunning) {
D.assert(false, "Try to record the stopwatch when it is already stopped");
}
this._stopwatch.Stop();
float frameCost = this._stopwatch.ElapsedMilliseconds;
this._stopwatch.Reset();
if (frameCost == 0) {
return;
}
this._curFrameId = (this._curFrameId + 1) % FrameBufferSize;
this._frames[this._curFrameId] = frameCost;
}
public float[] getFrames() {
if (!this._enabled) {
this._enabled = true;
}
return this._frames;
}
public int getCurFrame() {
return this._curFrameId;
}
}
}

/Runtime/service/performance_utils.cs.meta → /Runtime/flow/instrumentation.cs.meta

正在加载...
取消
保存