浏览代码

navigation

/main
fzhangtj 6 年前
当前提交
a3aea510
共有 25 个文件被更改,包括 2670 次插入712 次删除
  1. 9
      Runtime/animation/tween.cs
  2. 30
      Runtime/engine/WidgetCanvas.cs
  3. 6
      Runtime/promise/Promise.cs
  4. 250
      Runtime/rendering/proxy_box.cs
  5. 213
      Runtime/widgets/app.cs
  6. 63
      Runtime/widgets/basic.cs
  7. 5
      Runtime/widgets/binding.cs
  8. 2
      Runtime/widgets/focus_manager.cs
  9. 940
      Runtime/widgets/navigator.cs
  10. 157
      Runtime/widgets/overlay.cs
  11. 6
      Runtime/widgets/page_storage.cs
  12. 9
      Runtime/widgets/widget_inspector.cs
  13. 7
      Samples/UIWidgetSample/TextInputCanvas.cs
  14. 48
      Runtime/widgets/modal_barrier.cs
  15. 11
      Runtime/widgets/modal_barrier.cs.meta
  16. 89
      Runtime/widgets/pages.cs
  17. 11
      Runtime/widgets/pages.cs.meta
  18. 757
      Runtime/widgets/routes.cs
  19. 11
      Runtime/widgets/routes.cs.meta
  20. 133
      Runtime/widgets/transitions.cs
  21. 11
      Runtime/widgets/transitions.cs.meta
  22. 502
      Samples/UIWidgetSample/Navigation.unity
  23. 7
      Samples/UIWidgetSample/Navigation.unity.meta
  24. 94
      Samples/UIWidgetSample/NavigationCanvas.cs
  25. 11
      Samples/UIWidgetSample/NavigationCanvas.cs.meta

9
Runtime/animation/tween.cs


}
}
public class OffsetTween : Tween<Offset> {
public OffsetTween(Offset begin, Offset end) : base(begin: begin, end: end) {
}
public override Offset lerp(double t) {
return (this.begin + (this.end - this.begin) * t);
}
}
public class CurveTween : Animatable<double> {
public CurveTween(Curve curve = null) {
D.assert(curve != null);

30
Runtime/engine/WidgetCanvas.cs


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

this._windowAdapter = new UIWidgetWindowAdapter(this);
this._windowAdapter.OnEnable();
var root = new WidgetsApp(home: this.getWidget(), window: this._windowAdapter);
var root = new WidgetsApp(home: this.getWidget(),
window: this._windowAdapter,
routes: this.routes,
pageRouteBuilder: this.pageRouteBuilder,
onGenerateRoute: this.onGenerateRoute,
onUnknownRoute: this.onUnknownRoute);
protected virtual Dictionary<string, WidgetBuilder> routes => null;
protected virtual string initialRoute => null;
protected virtual RouteFactory onGenerateRoute => null;
protected virtual RouteFactory onUnknownRoute => null;
protected virtual PageRouteFactory pageRouteBuilder => (RouteSettings settings, WidgetBuilder builder) =>
new PageRouteBuilder(
settings: settings,
pageBuilder: (BuildContext context, Unity.UIWidgets.animation.Animation<double> animation,
Unity.UIWidgets.animation.Animation<double> secondaryAnimation) => builder(context)
);
protected override void OnDisable() {
D.assert(this._windowAdapter != null);
this._windowAdapter.OnDisable();

protected abstract Widget getWidget();
protected virtual Widget getWidget() {
return null;
}
internal void applyRenderTexture(Rect screenRect, Texture texture, Material mat) {
this.texture = texture;

6
Runtime/promise/Promise.cs


/// Gets the id of the promise, useful for referencing the promise during runtime.
/// </summary>
int Id { get; }
bool isCompleted { get; }
/// <summary>
/// Set the name of the promise, useful for debugging.

/// </summary>
public int Id {
get { return this.id; }
}
public bool isCompleted {
get { return this.CurState != PromiseState.Pending; }
}
readonly int id;

250
Runtime/rendering/proxy_box.cs


using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;

int _alpha;
static int _getAlphaFromOpacity(double opacity) {
internal static int _getAlphaFromOpacity(double opacity) {
return (opacity * 255).round();
}

}
}
public class RenderAnimatedOpacity : RenderProxyBox {
public RenderAnimatedOpacity(
Animation<double> opacity = null,
RenderBox child = null
) : base(child) {
D.assert(opacity != null);
this.opacity = opacity;
}
int _alpha;
protected override bool alwaysNeedsCompositing => this.child != null && this._currentlyNeedsCompositing;
bool _currentlyNeedsCompositing;
Animation<double> _opacity;
public Animation<double> opacity {
get => this._opacity;
set {
D.assert(value != null);
if (this._opacity == value)
return;
if (this.attached && this._opacity != null)
this._opacity.removeListener(this._updateOpacity);
this._opacity = value;
if (this.attached)
this._opacity.addListener(this._updateOpacity);
this._updateOpacity();
}
}
public override void attach(object owner) {
base.attach(owner);
this._opacity.addListener(this._updateOpacity);
this._updateOpacity(); // in case it changed while we weren't listening
}
public override void detach() {
this._opacity.removeListener(this._updateOpacity);
base.detach();
}
public void _updateOpacity() {
var oldAlpha = this._alpha;
this._alpha = RenderOpacity._getAlphaFromOpacity(this._opacity.value.clamp(0.0, 1.0));
if (oldAlpha != this._alpha) {
bool didNeedCompositing = this._currentlyNeedsCompositing;
this._currentlyNeedsCompositing = this._alpha > 0 &&this. _alpha < 255;
if (this.child != null && didNeedCompositing != this._currentlyNeedsCompositing)
this.markNeedsCompositingBitsUpdate();
this.markNeedsPaint();
}
}
public override void paint(PaintingContext context, Offset offset) {
if (this.child != null) {
if (this._alpha == 0)
return;
if (this._alpha == 255) {
context.paintChild(this.child, offset);
return;
}
D.assert(this.needsCompositing);
context.pushOpacity(offset, this._alpha, base.paint);
}
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<Animation<double>>("opacity", this.opacity));
}
}
public enum DecorationPosition {
background,
foreground,

}
}
public class RenderFractionalTranslation : RenderProxyBox {
public RenderFractionalTranslation(
Offset translation = null,
bool transformHitTests = true,
RenderBox child = null
) : base(child: child) {
D.assert(translation != null);
this._translation = translation;
this.transformHitTests = transformHitTests;
}
public Offset translation {
get => this._translation;
set {
D.assert(value != null);
if (this._translation == value)
return;
this._translation = value;
this.markNeedsPaint();
}
}
Offset _translation;
public override bool hitTest(HitTestResult result, Offset position) {
return this.hitTestChildren(result, position: position);
}
public bool transformHitTests;
protected override bool hitTestChildren(HitTestResult result, Offset position) {
D.assert(!this.debugNeedsLayout);
if (this.transformHitTests) {
position = new Offset(
position.dx - this.translation.dx * this.size.width,
position.dy - this.translation.dy * this.size.height
);
}
return base.hitTestChildren(result, position: position);
}
public override void paint(PaintingContext context, Offset offset) {
D.assert(!this.debugNeedsLayout);
if (this.child != null) {
base.paint(context, new Offset(
offset.dx + this.translation.dx * this.size.width,
offset.dy + this.translation.dy * this.size.height
));
}
}
public override void applyPaintTransform(RenderObject child, ref Matrix3 transform) {
transform = Matrix3.makeTrans((float)(this.translation.dx * this.size.width),
(float)(this.translation.dy * this.size.height)) * transform;
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<Offset>("translation", this.translation));
properties.add(new DiagnosticsProperty<bool>("transformHitTests", this.transformHitTests));
}
}
public delegate void PointerDownEventListener(PointerDownEvent evt);
public delegate void PointerMoveEventListener(PointerMoveEvent evt);

properties.add(new DiagnosticsProperty<bool>("ignoring", this.ignoring));
}
}
public class RenderOffstage : RenderProxyBox {
public RenderOffstage(bool offstage = true,
RenderBox child = null): base(child) {
this._offstage = offstage;
}
public bool offstage {
get => this._offstage;
set {
if (value == this._offstage) {
return;
}
this._offstage = value;
this.markNeedsLayoutForSizedByParentChange();
}
}
bool _offstage;
protected override double computeMinIntrinsicWidth(double height) {
if (this.offstage)
return 0.0;
return base.computeMinIntrinsicWidth(height);
}
protected override double computeMaxIntrinsicWidth(double height) {
if (this.offstage)
return 0.0;
return base.computeMaxIntrinsicWidth(height);
}
protected override double computeMinIntrinsicHeight(double width) {
if (this.offstage)
return 0.0;
return base.computeMinIntrinsicHeight(width);
}
protected override double computeMaxIntrinsicHeight(double width) {
if (this.offstage)
return 0.0;
return base.computeMaxIntrinsicHeight(width);
}
protected override double? computeDistanceToActualBaseline(TextBaseline baseline) {
if (this.offstage)
return null;
return base.computeDistanceToActualBaseline(baseline);
}
protected override bool sizedByParent => this.offstage;
protected override void performResize() {
D.assert(this.offstage);
this.size = this.constraints.smallest;
}
protected override void performLayout() {
if (this.offstage) {
this.child?.layout(this.constraints);
} else {
base.performLayout();
}
}
public override bool hitTest(HitTestResult result, Offset position) {
return !this.offstage && base.hitTest(result, position: position);
}
public override void paint(PaintingContext context, Offset offset) {
if (this.offstage)
return;
base.paint(context, offset);
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<bool>("offstage", this.offstage));
}
public override List<DiagnosticsNode> debugDescribeChildren() {
if (this.child == null)
return new List<DiagnosticsNode>();
return new List<DiagnosticsNode> {
this.child.toDiagnosticsNode(
name: "child",
style: this.offstage ? DiagnosticsTreeStyle.offstage : DiagnosticsTreeStyle.sparse
),
};
}
}
public class RenderAbsorbPointer : RenderProxyBox {

) : base(child)
{
_absorbing = absorbing;
this._absorbing = absorbing;
get { return _absorbing; }
get { return this._absorbing; }
_absorbing = value;
this._absorbing = value;
return absorbing
? size.contains(position)
return this.absorbing
? this.size.contains(position)
: base.hitTest(result, position: position);
}

properties.add(new DiagnosticsProperty<bool>("absorbing", absorbing));
properties.add(new DiagnosticsProperty<bool>("absorbing", this.absorbing));
}
}
}

213
Runtime/widgets/app.cs


using System.Collections.Generic;
using RSG;
using Color = Unity.UIWidgets.ui.Color;
public delegate PageRoute PageRouteFactory(RouteSettings settings, WidgetBuilder builder);
public readonly TransitionBuilder builder;
public readonly Widget home;
public readonly string initialRoute;
public readonly GlobalKey<NavigatorState> navigatorKey;
public readonly List<NavigatorObserver> navigatorObservers;
public readonly RouteFactory onGenerateRoute;
public readonly RouteFactory onUnknownRoute;
public readonly PageRouteFactory pageRouteBuilder;
public readonly Dictionary<string, WidgetBuilder> routes;
public readonly Widget home;
GlobalKey<NavigatorState> navigatorKey = null,
RouteFactory onGenerateRoute = null,
RouteFactory onUnknownRoute = null,
PageRouteFactory pageRouteBuilder = null,
List<NavigatorObserver> navigatorObservers = null,
string initialRoute = null,
Dictionary<string, WidgetBuilder> routes = null,
TransitionBuilder builder = null,
Window window = null,
Widget home = null
) : base(key) {

this.navigatorKey = navigatorKey;
this.onGenerateRoute = onGenerateRoute;
this.onUnknownRoute = onUnknownRoute;
this.pageRouteBuilder = pageRouteBuilder;
this.routes = routes ?? new Dictionary<string, WidgetBuilder>();
this.navigatorObservers = navigatorObservers ?? new List<NavigatorObserver>();
this.initialRoute = initialRoute;
this.builder = builder;
D.assert(
home == null ||
!this.routes.ContainsKey(Navigator.defaultRouteName),
"If the home property is specified, the routes table " +
"cannot include an entry for \" / \", since it would be redundant."
);
D.assert(
builder != null ||
home != null ||
routes.ContainsKey(Navigator.defaultRouteName) ||
onGenerateRoute != null ||
onUnknownRoute != null,
"Either the home property must be specified, " +
"or the routes table must include an entry for \"/\", " +
"or there must be on onGenerateRoute callback specified, " +
"or there must be an onUnknownRoute callback specified, " +
"or the builder property must be specified, " +
"because otherwise there is nothing to fall back on if the " +
"app is started with an intent that specifies an unknown route."
);
D.assert(
builder != null ||
onGenerateRoute != null ||
pageRouteBuilder != null,
"If neither builder nor onGenerateRoute are provided, the " +
"pageRouteBuilder must be specified so that the default handler " +
"will know what kind of PageRoute transition to build."
);
}
public override State createState() {

public class WindowProvider : InheritedWidget {
public readonly Window window;
base(key: key, child: child) {
base(key, child) {
public readonly Window window;
WindowProvider provider = (WindowProvider) context.inheritFromWidgetOfExactType(typeof(WindowProvider));
if (provider == null) {
throw new UIWidgetsError("WindowProvider is missing");
}
var provider = (WindowProvider) context.inheritFromWidgetOfExactType(typeof(WindowProvider));
if (provider == null) throw new UIWidgetsError("WindowProvider is missing");
return provider.window;
}

}
}
class _WidgetsAppState : State<WidgetsApp>, WidgetsBindingObserver {
internal class _WidgetsAppState : State<WidgetsApp>, WidgetsBindingObserver {
GlobalKey<NavigatorState> _navigator;
public IPromise<bool> didPopRoute() {
D.assert(this.mounted);
var navigator = this._navigator?.currentState;
if (navigator == null)
return Promise<bool>.Resolved(false);
return navigator.maybePop();
}
public IPromise<bool> didPushRoute(string route) {
D.assert(this.mounted);
var navigator = this._navigator?.currentState;
if (navigator == null)
return Promise<bool>.Resolved(false);
navigator.pushNamed(route);
return Promise<bool>.Resolved(true);
}
public void didChangeMetrics() {
this.setState();
}
public void didChangeTextScaleFactor() {
this.setState();
}
public void didChangeLocales(List<Locale> locale) {
// TODO: support locales.
}
this._updateNavigator();
D.assert(() => {
WidgetInspectorService.instance.inspectorShowCallback += this.inspectorShowChanged;
return true;

}
public override void didUpdateWidget(StatefulWidget oldWidget) {
base.didUpdateWidget(oldWidget);
if (this.widget.navigatorKey != ((WidgetsApp) oldWidget).navigatorKey) this._updateNavigator();
}
public override void dispose() {

base.dispose();
}
void inspectorShowChanged() {
this.setState();
void _updateNavigator() {
this._navigator = this.widget.navigatorKey ?? new GlobalObjectKey<NavigatorState>(this);
public void didChangeMetrics() {
this.setState();
Route _onGenerateRoute(RouteSettings settings) {
var name = settings.name;
var pageContentBuilder = name == Navigator.defaultRouteName && this.widget.home != null
? context => this.widget.home
: this.widget.routes[name];
if (pageContentBuilder != null) {
D.assert(this.widget.pageRouteBuilder != null,
"The default onGenerateRoute handler for WidgetsApp must have a " +
"pageRouteBuilder set if the home or routes properties are set.");
var route = this.widget.pageRouteBuilder(
settings,
pageContentBuilder
);
D.assert(route != null,
"The pageRouteBuilder for WidgetsApp must return a valid non-null Route.");
return route;
}
if (this.widget.onGenerateRoute != null)
return this.widget.onGenerateRoute(settings);
return null;
public void didChangeTextScaleFactor() {
this.setState();
Route _onUnknownRoute(RouteSettings settings) {
D.assert(() => {
if (this.widget.onUnknownRoute == null)
throw new UIWidgetsError(
$"Could not find a generator for route {settings} in the {this.GetType()}.\n" +
$"Generators for routes are searched for in the following order:\n" +
" 1. For the \"/\" route, the \"home\" property, if non-null, is used.\n" +
" 2. Otherwise, the \"routes\" table is used, if it has an entry for " +
"the route.\n" +
" 3. Otherwise, onGenerateRoute is called. It should return a " +
"non-null value for any valid route not handled by \"home\" and \"routes\".\n" +
" 4. Finally if all else fails onUnknownRoute is called.\n" +
"Unfortunately, onUnknownRoute was not set."
);
return true;
});
var result = this.widget.onUnknownRoute(settings);
D.assert(() => {
if (result == null)
throw new UIWidgetsError(
"The onUnknownRoute callback returned null.\n" +
"When the $runtimeType requested the route $settings from its " +
"onUnknownRoute callback, the callback returned null. Such callbacks " +
"must never return null."
);
return true;
});
return result;
public void didChangeLocales(List<Locale> locale) {
// TODO: support locales.
void inspectorShowChanged() {
this.setState();
Widget result = this.widget.home;
Widget navigator = null;
if (this._navigator != null)
navigator = new Navigator(
key: this._navigator,
initialRoute: this.widget.initialRoute ?? Navigator.defaultRouteName,
onGenerateRoute: this._onGenerateRoute,
onUnknownRoute: this._onUnknownRoute,
observers: this.widget.navigatorObservers
);
Widget result;
if (this.widget.builder != null) {
result = new Builder(
builder: _context => { return this.widget.builder(_context, navigator); }
);
}
else {
D.assert(navigator != null);
result = navigator;
}
if (WidgetInspectorService.instance.debugShowInspector) {
if (WidgetInspectorService.instance.debugShowInspector)
}
return true;
});

}
Widget _InspectorSelectButtonBuilder(BuildContext context, VoidCallback onPressed) {
return new _InspectorSelectButton(onPressed: onPressed);
return new _InspectorSelectButton(onPressed);
class _InspectorSelectButton : StatelessWidget {
internal class _InspectorSelectButton : StatelessWidget {
public readonly GestureTapCallback onPressed;
) : base(key: key) {
) : base(key) {
public readonly GestureTapCallback onPressed;
public override Widget build(BuildContext context) {
return new GestureDetector(

);
}
}
}
}

63
Runtime/widgets/basic.cs


}
}
public class Offstage : SingleChildRenderObjectWidget {
public Offstage(Key key = null, bool offstage = true, Widget child = null):base(key: key, child: child) {
this.offstage = offstage;
}
public readonly bool offstage;
public override RenderObject createRenderObject(BuildContext context) {
return new RenderOffstage(offstage: this.offstage);
}
public override void updateRenderObject(BuildContext context, RenderObject renderObject) {
((RenderOffstage)renderObject).offstage = this.offstage;
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<bool>("offstage", this.offstage));
}
public override Element createElement() => new _OffstageElement(this);
}
class _OffstageElement: SingleChildRenderObjectElement {
internal _OffstageElement(Offstage widget) : base(widget) {}
new Offstage widget => (Offstage) base.widget;
public override void debugVisitOnstageChildren(ElementVisitor visitor) {
if (!this.widget.offstage)
base.debugVisitOnstageChildren(visitor);
}
}
public class AspectRatio : SingleChildRenderObjectWidget {
public AspectRatio(
Key key = null,

public static Positioned directional(Widget child, TextDirection textDirection, Key key = null,
double? start = null, double? top = null,
double? end = null, double? bottom = null, double? width = null, double? height = null) {
D.assert(textDirection != null);
double? left = null;
double? right = null;
switch (textDirection) {

}
}
public class FractionalTranslation : SingleChildRenderObjectWidget {
public FractionalTranslation(Key key = null, Offset translation = null,
bool transformHitTests = true, Widget child = null) : base(key: key, child: child) {
this.translation = translation;
this.transformHitTests = transformHitTests;
}
public readonly Offset translation;
public readonly bool transformHitTests;
public override RenderObject createRenderObject(BuildContext context) {
return new RenderFractionalTranslation(
translation: this.translation,
transformHitTests: this.transformHitTests
);
}
public override void updateRenderObject(BuildContext context, RenderObject renderObject) {
((RenderFractionalTranslation)renderObject).translation = this.translation;
((RenderFractionalTranslation)renderObject).transformHitTests = this.transformHitTests;
}
}
public class Align : SingleChildRenderObjectWidget {
public Align(
Key key = null,

public override RenderObject createRenderObject(BuildContext context) {
return new RenderAbsorbPointer(
absorbing: absorbing
absorbing: this.absorbing
((RenderAbsorbPointer) renderObject).absorbing = absorbing;
((RenderAbsorbPointer) renderObject).absorbing = this.absorbing;
properties.add(new DiagnosticsProperty<bool>("absorbing", absorbing));
properties.add(new DiagnosticsProperty<bool>("absorbing", this.absorbing));
}
}

5
Runtime/widgets/binding.cs


using System;
using System.Collections.Generic;
using RSG;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;

void didChangeTextScaleFactor();
void didChangeLocales(List<Locale> locale);
IPromise<bool> didPopRoute();
IPromise<bool> didPushRoute(string route);
}
public class WidgetsBinding : RendererBinding {

2
Runtime/widgets/focus_manager.cs


D.assert(child != this);
D.assert(child != this._firstChild);
D.assert(child != this._lastChild);
D.assert(child == null);
D.assert(child._parent == null);
D.assert(child._manager == null);
D.assert(child._nextSibling == null);
D.assert(child._previousSibling == null);

940
Runtime/widgets/navigator.cs


using Unity.UIWidgets.rendering;
using Unity.UIWidgets.scheduler;
namespace Unity.UIWidgets.widgets
{
namespace Unity.UIWidgets.widgets {
public enum RoutePopDisposition
{
public delegate IPromise<bool> WillPopCallback();
public enum RoutePopDisposition {
public abstract class Route
{
public Route(RouteSettings settings = null)
{
public abstract class Route {
public readonly RouteSettings settings;
internal NavigatorState _navigator;
public Route(RouteSettings settings = null) {
public NavigatorState navigator
{
get { return _navigator; }
}
public NavigatorState navigator => this._navigator;
internal NavigatorState _navigator;
public virtual List<OverlayEntry> overlayEntries => new List<OverlayEntry>();
public readonly RouteSettings settings;
public virtual bool willHandlePopInternally => false;
public virtual List<OverlayEntry> overlayEntries
{
get { return new List<OverlayEntry>(); }
}
public object currentResult => default;
protected internal virtual void install(OverlayEntry insertionPoint)
{
}
public Promise<object> popped { get; } = new Promise<object>();
protected internal virtual TickerFuture didPush()
{
return TickerFutureImpl.complete();
}
public bool isCurrent => this._navigator != null && this._navigator._history.last() == this;
protected internal virtual void didReplace(Route oldRoute)
{
}
public bool isFirst => this._navigator != null && this._navigator._history.first() == this;
public virtual IPromise<RoutePopDisposition> willPop()
{
return Promise<RoutePopDisposition>.Resolved(isFirst
? RoutePopDisposition.bubble
: RoutePopDisposition.pop);
}
public bool isActive => this._navigator != null && this._navigator._history.Contains(this);
public virtual bool willHandlePopInternally
{
get { return false; }
protected internal virtual void install(OverlayEntry insertionPoint) {
public object currentResult
{
get { return default; }
protected internal virtual TickerFuture didPush() {
return TickerFutureImpl.complete();
public Promise<object> popped
{
get { return _popCompleter; }
protected internal virtual void didReplace(Route oldRoute) {
readonly Promise<object> _popCompleter = new Promise<object>();
public virtual IPromise<RoutePopDisposition> willPop() {
return Promise<RoutePopDisposition>.Resolved(this.isFirst
? RoutePopDisposition.bubble
: RoutePopDisposition.pop);
}
protected internal virtual bool didPop(object result)
{
didComplete(result);
protected internal virtual bool didPop(object result) {
this.didComplete(result);
protected internal virtual void didComplete(object result)
{
_popCompleter.Resolve(result);
protected internal virtual void didComplete(object result) {
this.popped.Resolve(result);
protected internal virtual void didPopNext(Route nextRoute)
{
protected internal virtual void didPopNext(Route nextRoute) {
protected internal virtual void didChangeNext(Route nextRoute)
{
protected internal virtual void didChangeNext(Route nextRoute) {
protected internal virtual void didChangePrevious(Route previousRoute)
{
protected internal virtual void didChangePrevious(Route previousRoute) {
protected internal virtual void changedInternalState()
{
protected internal virtual void changedInternalState() {
protected internal virtual void changedExternalState()
{
protected internal virtual void changedExternalState() {
protected internal virtual void dispose()
{
_navigator = null;
protected internal virtual void dispose() {
this._navigator = null;
}
bool isCurrent
{
get { return _navigator != null && _navigator._history.last() == this; }
}
public class RouteSettings {
public readonly bool isInitialRoute;
bool isFirst
{
get { return _navigator != null && _navigator._history.first() == this; }
}
public readonly string name;
private bool isActive
{
get { return _navigator != null && _navigator._history.Contains(this); }
}
}
public class RouteSettings
{
public RouteSettings(string name = null, bool isInitialRoute = false)
{
public RouteSettings(string name = null, bool isInitialRoute = false) {
RouteSettings copyWith(string name = null, bool? isInitialRoute = null)
{
RouteSettings copyWith(string name = null, bool? isInitialRoute = null) {
name: name ?? this.name,
isInitialRoute: isInitialRoute ?? this.isInitialRoute
name ?? this.name,
isInitialRoute ?? this.isInitialRoute
public readonly string name;
public readonly bool isInitialRoute;
public override string ToString()
{
return $"\"{name}\"";
public override string ToString() {
return $"\"{this.name}\"";
public class NavigatorObserver
{
public NavigatorState navigator
{
get { return _navigator; }
}
public class NavigatorObserver {
internal NavigatorState _navigator;
internal NavigatorState _navigator;
public NavigatorState navigator => this._navigator;
public virtual void didPush(Route route, Route previousRoute)
{
public virtual void didPush(Route route, Route previousRoute) {
public virtual void didPop(Route route, Route previousRoute)
{
public virtual void didPop(Route route, Route previousRoute) {
public virtual void didRemove(Route route, Route previousRoute)
{
public virtual void didRemove(Route route, Route previousRoute) {
public virtual void didReplace(Route newRoute = null, Route oldRoute = null)
{
public virtual void didReplace(Route newRoute = null, Route oldRoute = null) {
public virtual void didStartUserGesture(Route route, Route previousRoute)
{
public virtual void didStartUserGesture(Route route, Route previousRoute) {
public virtual void didStopUserGesture()
{
public virtual void didStopUserGesture() {
public class Navigator : StatefulWidget
{
public class Navigator : StatefulWidget {
/// The default name for the [initialRoute].
///
/// See also:
///
/// * [dart:ui.Window.defaultRouteName], which reflects the route that the
/// application was started with.
public static string defaultRouteName = "/";
public readonly string initialRoute;
public readonly List<NavigatorObserver> observers;
public readonly RouteFactory onGenerateRoute;
public readonly RouteFactory onUnknownRoute;
List<NavigatorObserver> observers = null) : base(key)
{
List<NavigatorObserver> observers = null) : base(key) {
D.assert(onGenerateRoute != null);
this.initialRoute = initialRoute;
this.onUnknownRoute = onUnknownRoute;

public readonly String initialRoute;
public readonly RouteFactory onGenerateRoute;
public readonly RouteFactory onUnknownRoute;
public readonly List<NavigatorObserver> observers;
/// The default name for the [initialRoute].
///
/// See also:
///
/// * [dart:ui.Window.defaultRouteName], which reflects the route that the
/// application was started with.
public static string defaultRouteName = "/";
public static IPromise<object> pushName(BuildContext context, String routeName)
{
return Navigator.of(context).pushNamed(routeName);
public static IPromise<object> pushName(BuildContext context, string routeName) {
return of(context).pushNamed(routeName);
public static IPromise<object> pushReplacementNamed(BuildContext context, String routeName,
object result = null)
{
return Navigator.of(context).pushReplacementNamed(routeName, result: result);
public static IPromise<object> pushReplacementNamed(BuildContext context, string routeName,
object result = null) {
return of(context).pushReplacementNamed(routeName, result);
public static IPromise<object> popAndPushNamed(BuildContext context, String routeName, object result = null)
{
return Navigator.of(context).popAndPushNamed(routeName, result: result);
public static IPromise<object> popAndPushNamed(BuildContext context, string routeName, object result = null) {
return of(context).popAndPushNamed(routeName, result);
public static IPromise<object> pushNamedAndRemoveUntil(BuildContext context, String newRouteName,
RoutePredicate predicate)
{
return Navigator.of(context).pushNamedAndRemoveUntil(newRouteName, predicate);
public static IPromise<object> pushNamedAndRemoveUntil(BuildContext context, string newRouteName,
RoutePredicate predicate) {
return of(context).pushNamedAndRemoveUntil(newRouteName, predicate);
public static IPromise<object> push(BuildContext context, Route route)
{
return Navigator.of(context).push(route);
public static IPromise<object> push(BuildContext context, Route route) {
return of(context).push(route);
public static IPromise<object> pushReplacement(BuildContext context, Route newRoute, object result = null)
{
return Navigator.of(context).pushReplacement(newRoute, result: result);
public static IPromise<object> pushReplacement(BuildContext context, Route newRoute, object result = null) {
return of(context).pushReplacement(newRoute, result);
RoutePredicate predicate)
{
return Navigator.of(context).pushAndRemoveUntil(newRoute, predicate);
RoutePredicate predicate) {
return of(context).pushAndRemoveUntil(newRoute, predicate);
public static void replace(BuildContext context, Route oldRoute, Route newRoute)
{
Navigator.of(context).replace(oldRoute: oldRoute, newRoute: newRoute);
public static void replace(BuildContext context, Route oldRoute, Route newRoute) {
of(context).replace(oldRoute, newRoute);
public static void replaceRouteBelow(BuildContext context, Route anchorRoute = null, Route newRoute = null)
{
Navigator.of(context).replaceRouteBelow(anchorRoute: anchorRoute, newRoute: newRoute);
public static void replaceRouteBelow(BuildContext context, Route anchorRoute = null, Route newRoute = null) {
of(context).replaceRouteBelow(anchorRoute, newRoute);
public static IPromise<bool> maybePop(BuildContext context, object result = null)
{
return Navigator.of(context).maybePop(result);
public static IPromise<bool> maybePop(BuildContext context, object result = null) {
return of(context).maybePop(result);
public static bool pop(BuildContext context, object result = null)
{
return Navigator.of(context).pop(result);
public static bool pop(BuildContext context, object result = null) {
return of(context).pop(result);
public static void popUntil(BuildContext context, RoutePredicate predicate)
{
Navigator.of(context).popUntil(predicate);
public static void popUntil(BuildContext context, RoutePredicate predicate) {
of(context).popUntil(predicate);
public static void removeRoute(BuildContext context, Route route)
{
Navigator.of(context).removeRoute(route);
public static void removeRoute(BuildContext context, Route route) {
of(context).removeRoute(route);
static void removeRouteBelow(BuildContext context, Route anchorRoute)
{
Navigator.of(context).removeRouteBelow(anchorRoute);
static void removeRouteBelow(BuildContext context, Route anchorRoute) {
of(context).removeRouteBelow(anchorRoute);
}
public static NavigatorState of(

)
{
NavigatorState navigator = rootNavigator
? (NavigatorState) (context.rootAncestorStateOfType(new TypeMatcher<NavigatorState>()))
) {
var navigator = rootNavigator
? (NavigatorState) context.rootAncestorStateOfType(new TypeMatcher<NavigatorState>())
D.assert(() =>
{
D.assert(() => {
{
}
return true;
});

public override State createState()
{
public override State createState() {
public class NavigatorState : TickerProviderStateMixin<Navigator>
{
public class NavigatorState : TickerProviderStateMixin<Navigator> {
internal readonly List<Route> _history = new List<Route>();
readonly internal List<Route> _history = new List<Route>();
List<OverlayEntry> _initialOverlayEntries = new List<OverlayEntry>();
readonly HashSet<int> _activePointers = new HashSet<int>();
bool _debugLocked;
readonly List<OverlayEntry> _initialOverlayEntries = new List<OverlayEntry>();
int _userGesturesInProgress;
public override void initState()
{
public OverlayState overlay => this._overlayKey.currentState;
OverlayEntry _currentOverlayEntry {
get {
var route = this._history.FindLast(r => r.overlayEntries.isNotEmpty());
return route?.overlayEntries.last();
}
}
public bool userGestureInProgress => this._userGesturesInProgress > 0;
public override void initState() {
foreach (var observer in widget.observers)
{
foreach (var observer in this.widget.observers) {
string initialRouteName = widget.initialRoute ?? Navigator.defaultRouteName;
if (initialRouteName.StartsWith("/") && initialRouteName.Length > 1)
{
var initialRouteName = this.widget.initialRoute ?? Navigator.defaultRouteName;
if (initialRouteName.StartsWith("/") && initialRouteName.Length > 1) {
List<String> plannedInitialRouteNames = new List<String>
{
var plannedInitialRouteNames = new List<string> {
List<Route> plannedInitialRoutes = new List<Route>
{
_routeNamed(Navigator.defaultRouteName, allowNull: true)
var plannedInitialRoutes = new List<Route> {
this._routeNamed(Navigator.defaultRouteName, true)
if (initialRouteName.isNotEmpty())
{
string routeName = "";
foreach (var part in routeParts)
{
if (initialRouteName.isNotEmpty()) {
var routeName = "";
foreach (var part in routeParts) {
plannedInitialRoutes.Add(_routeNamed(routeName, allowNull: true));
plannedInitialRoutes.Add(this._routeNamed(routeName, true));
if (plannedInitialRoutes.Contains(null))
{
D.assert(() =>
{
if (plannedInitialRoutes.Contains(null)) {
D.assert(() => {
exception: new Exception(
new Exception(
"Could not navigate to initial route.\n" +
$"The requested route name was: \"{initialRouteName}\n" +
"The following routes were therefore attempted:\n" +

$"ignored and \"{Navigator.defaultRouteName}\" will be used instead.")));
return true;
});
push(_routeNamed(Navigator.defaultRouteName));
this.push(this._routeNamed(Navigator.defaultRouteName));
else
{
plannedInitialRoutes.Each((route) => { push(route); });
else {
plannedInitialRoutes.Each(route => { this.push(route); });
else
{
else {
if (initialRouteName != Navigator.defaultRouteName)
{
route = _routeNamed(initialRouteName, allowNull: true);
}
if (initialRouteName != Navigator.defaultRouteName) route = this._routeNamed(initialRouteName, true);
route = route ?? _routeNamed(Navigator.defaultRouteName);
push(route);
route = route ?? this._routeNamed(Navigator.defaultRouteName);
this.push(route);
foreach (var route in _history)
{
_initialOverlayEntries.AddRange(route.overlayEntries);
}
foreach (var route in this._history) this._initialOverlayEntries.AddRange(route.overlayEntries);
public override void didUpdateWidget(StatefulWidget oldWidget)
{
public override void didUpdateWidget(StatefulWidget oldWidget) {
if (((Navigator) oldWidget).observers != widget.observers)
{
foreach (var observer in ((Navigator) oldWidget).observers)
{
observer._navigator = null;
}
if (((Navigator) oldWidget).observers != this.widget.observers) {
foreach (var observer in ((Navigator) oldWidget).observers) observer._navigator = null;
foreach (var observer in widget.observers)
{
foreach (var observer in this.widget.observers) {
foreach (var route in _history)
{
route.changedExternalState();
}
foreach (var route in this._history) route.changedExternalState();
public override void dispose()
{
D.assert(!_debugLocked);
D.assert(() =>
{
_debugLocked = true;
public override void dispose() {
D.assert(!this._debugLocked);
D.assert(() => {
this._debugLocked = true;
foreach (var observer in widget.observers)
{
observer._navigator = null;
}
foreach (var observer in this.widget.observers) observer._navigator = null;
var doomed = _poppedRoutes.ToList();
doomed.AddRange(_history);
foreach (var route in doomed)
{
route.dispose();
}
var doomed = this._poppedRoutes.ToList();
doomed.AddRange(this._history);
foreach (var route in doomed) route.dispose();
_poppedRoutes.Clear();
_history.Clear();
focusScopeNode.detach();
this._poppedRoutes.Clear();
this._history.Clear();
this.focusScopeNode.detach();
D.assert(() =>
{
_debugLocked = false;
D.assert(() => {
this._debugLocked = false;
public OverlayState overlay
{
get { return _overlayKey.currentState; }
}
private OverlayEntry _currentOverlayEntry
{
get
{
var route = _history.FindLast((r) => r.overlayEntries.isNotEmpty());
return route?.overlayEntries.last();
}
}
bool _debugLocked = false;
Route _routeNamed(string name, bool allowNull = false)
{
D.assert(!_debugLocked);
Route _routeNamed(string name, bool allowNull = false) {
D.assert(!this._debugLocked);
var settings = new RouteSettings(name: name, isInitialRoute: _history.isEmpty());
Route route = widget.onGenerateRoute(settings);
if (route == null && !allowNull)
{
D.assert(() =>
{
if (widget.onUnknownRoute == null)
{
var settings = new RouteSettings(name, this._history.isEmpty());
var route = this.widget.onGenerateRoute(settings);
if (route == null && !allowNull) {
D.assert(() => {
if (this.widget.onUnknownRoute == null)
throw new UIWidgetsError(
"If a Navigator has no onUnknownRoute, then its onGenerateRoute must never return null.\n" +
$"When trying to build the route \"{name}\", onGenerateRoute returned null, but there was no " +

}
}
route = widget.onUnknownRoute(settings);
D.assert(() =>
{
if (route == null)
{
throw new UIWidgetsError(
"A Navigator\'s onUnknownRoute returned null.\n" +
$"When trying to build the route \"{name}\", both onGenerateRoute and onUnknownRoute returned " +
"null. The onUnknownRoute callback should never return null.\n" +
"The Navigator was:\n" +
$" {this}"
);
}
return true;
});
route = this.widget.onUnknownRoute(settings);
D.assert(() => {
if (route == null)
throw new UIWidgetsError(
"A Navigator\'s onUnknownRoute returned null.\n" +
$"When trying to build the route \"{name}\", both onGenerateRoute and onUnknownRoute returned " +
"null. The onUnknownRoute callback should never return null.\n" +
"The Navigator was:\n" +
$" {this}"
);
return true;
});
}
public Promise<object> pushNamed(String routeName)
{
return push(_routeNamed(routeName));
public Promise<object> pushNamed(string routeName) {
return this.push(this._routeNamed(routeName));
public Promise<object> pushReplacementNamed(String routeName, object result = null)
{
return pushReplacement(_routeNamed(routeName), result: result);
public Promise<object> pushReplacementNamed(string routeName, object result = null) {
return this.pushReplacement(this._routeNamed(routeName), result);
public Promise<object> popAndPushNamed(String routeName, object result = null)
{
pop(result);
return pushNamed(routeName);
public Promise<object> popAndPushNamed(string routeName, object result = null) {
this.pop(result);
return this.pushNamed(routeName);
public Promise<object> pushNamedAndRemoveUntil(string newRouteName, RoutePredicate predicate)
{
return pushAndRemoveUntil(_routeNamed(newRouteName), predicate);
public Promise<object> pushNamedAndRemoveUntil(string newRouteName, RoutePredicate predicate) {
return this.pushAndRemoveUntil(this._routeNamed(newRouteName), predicate);
public Promise<object> push(Route route)
{
D.assert(!_debugLocked);
D.assert(() =>
{
_debugLocked = true;
public Promise<object> push(Route route) {
D.assert(!this._debugLocked);
D.assert(() => {
this._debugLocked = true;
var oldRoute = _history.isNotEmpty() ? _history.last() : null;
var oldRoute = this._history.isNotEmpty() ? this._history.last() : null;
route.install(_currentOverlayEntry);
_history.Add(route);
route.install(this._currentOverlayEntry);
this._history.Add(route);
if (oldRoute != null)
{
if (oldRoute != null) {
foreach (var observer in widget.observers)
{
observer.didPush(route, oldRoute);
}
foreach (var observer in this.widget.observers) observer.didPush(route, oldRoute);
D.assert(() =>
{
_debugLocked = false;
D.assert(() => {
this._debugLocked = false;
_afterNavigation();
this._afterNavigation();
void _afterNavigation()
{
void _afterNavigation() {
public Promise<object> pushReplacement(Route newRoute, object result = null)
{
D.assert(!_debugLocked);
D.assert(() =>
{
_debugLocked = true;
public Promise<object> pushReplacement(Route newRoute, object result = null) {
D.assert(!this._debugLocked);
D.assert(() => {
this._debugLocked = true;
var oldRoute = _history.last();
var oldRoute = this._history.last();
var index = _history.Count - 1;
var index = this._history.Count - 1;
D.assert(_history.IndexOf(oldRoute) == index);
D.assert(this._history.IndexOf(oldRoute) == index);
newRoute.install(_currentOverlayEntry);
_history[index] = newRoute;
newRoute.didPush().whenCompleteOrCancel(() =>
{
newRoute.install(this._currentOverlayEntry);
this._history[index] = newRoute;
newRoute.didPush().whenCompleteOrCancel(() => {
if (mounted)
{
if (this.mounted) {
if (index > 0)
{
_history[index - 1].didChangeNext(newRoute);
newRoute.didChangePrevious(_history[index - 1]);
if (index > 0) {
this._history[index - 1].didChangeNext(newRoute);
newRoute.didChangePrevious(this._history[index - 1]);
foreach (var observer in widget.observers)
{
observer.didReplace(newRoute, oldRoute);
}
foreach (var observer in this.widget.observers) observer.didReplace(newRoute, oldRoute);
D.assert(() =>
{
_debugLocked = false;
D.assert(() => {
this._debugLocked = false;
_afterNavigation();
this._afterNavigation();
public Promise<object> pushAndRemoveUntil(Route newRoute, RoutePredicate predicate)
{
D.assert(!_debugLocked);
D.assert(() =>
{
_debugLocked = true;
public Promise<object> pushAndRemoveUntil(Route newRoute, RoutePredicate predicate) {
D.assert(!this._debugLocked);
D.assert(() => {
this._debugLocked = true;
List<Route> removedRoutes = new List<Route>();
while (_history.isNotEmpty() && !predicate(_history.last()))
{
var removedRoute = _history.last();
_history.RemoveAt(_history.Count - 1);
var removedRoutes = new List<Route>();
while (this._history.isNotEmpty() && !predicate(this._history.last())) {
var removedRoute = this._history.last();
this._history.RemoveAt(this._history.Count - 1);
D.assert(removedRoute != null && removedRoute._navigator == this);
D.assert(removedRoute.overlayEntries.isNotEmpty());
removedRoutes.Add(removedRoute);

D.assert(newRoute.overlayEntries.isEmpty());
var oldRoute = _history.isNotEmpty() ? _history.last() : null;
var oldRoute = this._history.isNotEmpty() ? this._history.last() : null;
newRoute.install(_currentOverlayEntry);
_history.Add(newRoute);
newRoute.didPush().whenCompleteOrCancel(() =>
{
if (mounted)
{
newRoute.install(this._currentOverlayEntry);
this._history.Add(newRoute);
newRoute.didPush().whenCompleteOrCancel(() => {
if (this.mounted)
{
route.dispose(); // todo not call didComplete? inconsistent with pushReplacement
}
}
route.dispose();
if (oldRoute != null)
{
oldRoute.didChangeNext(newRoute);
}
if (oldRoute != null) oldRoute.didChangeNext(newRoute);
foreach (var observer in widget.observers)
{
foreach (var observer in this.widget.observers) {
foreach (var removedRoute in removedRoutes)
{
observer.didRemove(removedRoute, oldRoute);
}
foreach (var removedRoute in removedRoutes) observer.didRemove(removedRoute, oldRoute);
D.assert(() =>
{
_debugLocked = false;
D.assert(() => {
this._debugLocked = false;
_afterNavigation();
this._afterNavigation();
public void replace(Route oldRoute = null, Route newRoute = null)
{
D.assert(!_debugLocked);
public void replace(Route oldRoute = null, Route newRoute = null) {
D.assert(!this._debugLocked);
D.assert(() =>
{
_debugLocked = true;
D.assert(() => {
this._debugLocked = true;
return true;
});
D.assert(oldRoute._navigator == this);

D.assert(!overlay.debugIsVisible(oldRoute.overlayEntries.last()));
int index = _history.IndexOf(oldRoute);
D.assert(!this.overlay.debugIsVisible(oldRoute.overlayEntries.last()));
var index = this._history.IndexOf(oldRoute);
_history[index] = newRoute;
this._history[index] = newRoute;
if (index + 1 < _history.Capacity)
{
newRoute.didChangeNext(_history[index + 1]);
_history[index + 1].didChangePrevious(newRoute);
if (index + 1 < this._history.Capacity) {
newRoute.didChangeNext(this._history[index + 1]);
this._history[index + 1].didChangePrevious(newRoute);
else
{
else {
if (index > 0)
{
_history[index - 1].didChangeNext(newRoute);
newRoute.didChangePrevious(_history[index - 1]);
if (index > 0) {
this._history[index - 1].didChangeNext(newRoute);
newRoute.didChangePrevious(this._history[index - 1]);
foreach (var observer in widget.observers)
{
observer.didReplace(newRoute: newRoute, oldRoute: oldRoute);
}
foreach (var observer in this.widget.observers) observer.didReplace(newRoute, oldRoute);
D.assert(() =>
{
_debugLocked = false;
D.assert(() => {
this._debugLocked = false;
public void replaceRouteBelow(Route anchorRoute = null, Route newRoute = null)
{
public void replaceRouteBelow(Route anchorRoute = null, Route newRoute = null) {
D.assert(_history.IndexOf(anchorRoute) > 0);
replace(oldRoute: _history[_history.IndexOf(anchorRoute) - 1], newRoute: newRoute);
D.assert(this._history.IndexOf(anchorRoute) > 0);
this.replace(this._history[this._history.IndexOf(anchorRoute) - 1], newRoute);
public bool canPop()
{
D.assert(_history.isNotEmpty);
return _history.Count > 1 || _history[0].willHandlePopInternally;
public bool canPop() {
D.assert(this._history.isNotEmpty);
return this._history.Count > 1 || this._history[0].willHandlePopInternally;
public IPromise<bool> maybePop(object result)
{
var route = _history.last();
public IPromise<bool> maybePop(object result = null) {
var route = this._history.last();
return route.willPop().Then<bool>((disposition) =>
{
if (disposition != RoutePopDisposition.bubble && mounted)
{
if (disposition == RoutePopDisposition.pop)
pop(result);
return route.willPop().Then(disposition => {
if (disposition != RoutePopDisposition.bubble && this.mounted) {
if (disposition == RoutePopDisposition.pop) this.pop(result);
return Promise<bool>.Resolved(true);
}

public bool pop(object result = null)
{
D.assert(!_debugLocked);
D.assert(() =>
{
_debugLocked = true;
public bool pop(object result = null) {
D.assert(!this._debugLocked);
D.assert(() => {
this._debugLocked = true;
var route = _history.last();
var route = this._history.last();
D.assert(() =>
{
D.assert(() => {
if (route.didPop(result ?? route.currentResult))
{
if (route.didPop(result ?? route.currentResult)) {
if (_history.Count > 1)
{
_history.removeLast();
if (this._history.Count > 1) {
this._history.removeLast();
if (route._navigator != null)
_poppedRoutes.Add(route);
_history.last().didPopNext(route);
foreach (var observer in widget.observers)
{
observer.didPop(route, _history.last());
}
if (route._navigator != null) this._poppedRoutes.Add(route);
this._history.last().didPopNext(route);
foreach (var observer in this.widget.observers) observer.didPop(route, this._history.last());
else
{
D.assert(() =>
{
_debugLocked = false;
else {
D.assert(() => {
this._debugLocked = false;
else
{
else {
D.assert(() =>
{
_debugLocked = false;
D.assert(() => {
this._debugLocked = false;
_afterNavigation();
this._afterNavigation();
public void popUntil(RoutePredicate predicate)
{
while (!predicate(_history.last()))
pop();
public void popUntil(RoutePredicate predicate) {
while (!predicate(this._history.last())) this.pop();
public void removeRoute(Route route)
{
public void removeRoute(Route route) {
D.assert(!_debugLocked);
D.assert(() =>
{
_debugLocked = true;
D.assert(!this._debugLocked);
D.assert(() => {
this._debugLocked = true;
int index = _history.IndexOf(route);
var index = this._history.IndexOf(route);
var previousRoute = index > 0 ? _history[index - 1] : null;
var nextRoute = (index + 1 < _history.Count) ? _history[index + 1] : null;
_history.RemoveAt(index);
var previousRoute = index > 0 ? this._history[index - 1] : null;
var nextRoute = index + 1 < this._history.Count ? this._history[index + 1] : null;
this._history.RemoveAt(index);
foreach (var observer in widget.observers)
{
observer.didRemove(route, previousRoute);
}
foreach (var observer in this.widget.observers) observer.didRemove(route, previousRoute);
D.assert(() =>
{
_debugLocked = false;
D.assert(() => {
this._debugLocked = false;
_afterNavigation();
this._afterNavigation();
public void removeRouteBelow(Route anchorRoute)
{
D.assert(!_debugLocked);
D.assert(() =>
{
_debugLocked = true;
public void removeRouteBelow(Route anchorRoute) {
D.assert(!this._debugLocked);
D.assert(() => {
this._debugLocked = true;
var index = _history.IndexOf(anchorRoute) - 1;
var index = this._history.IndexOf(anchorRoute) - 1;
var targetRoute = _history[index];
var targetRoute = this._history[index];
!overlay.debugIsVisible(targetRoute.overlayEntries.last()));
_history.RemoveAt(index);
var nextRoute = index < _history.Count ? _history[index] : null;
var previousRoute = index > 0 ? _history[index - 1] : null;
!this.overlay.debugIsVisible(targetRoute.overlayEntries.last()));
this._history.RemoveAt(index);
var nextRoute = index < this._history.Count ? this._history[index] : null;
var previousRoute = index > 0 ? this._history[index - 1] : null;
D.assert(() =>
{
_debugLocked = false;
D.assert(() => {
this._debugLocked = false;
public void finalizeRoute(Route route)
{
_poppedRoutes.Remove(route);
public void finalizeRoute(Route route) {
this._poppedRoutes.Remove(route);
public bool userGestureInProgress
{
get { return _userGesturesInProgress > 0; }
}
private int _userGesturesInProgress = 0;
public void didStartUserGesture()
{
_userGesturesInProgress += 1;
if (_userGesturesInProgress == 1)
{
var route = _history.last();
var previousRoute = !route.willHandlePopInternally && _history.Count > 1
? _history[_history.Count - 2]
public void didStartUserGesture() {
this._userGesturesInProgress += 1;
if (this._userGesturesInProgress == 1) {
var route = this._history.last();
var previousRoute = !route.willHandlePopInternally && this._history.Count > 1
? this._history[this._history.Count - 2]
foreach (var observer in widget.observers)
{
observer.didStartUserGesture(route, previousRoute);
}
foreach (var observer in this.widget.observers) observer.didStartUserGesture(route, previousRoute);
public void didStopUserGesture()
{
D.assert(_userGesturesInProgress > 0);
_userGesturesInProgress -= 1;
if (_userGesturesInProgress == 0)
{
foreach (var observer in widget.observers)
{
public void didStopUserGesture() {
D.assert(this._userGesturesInProgress > 0);
this._userGesturesInProgress -= 1;
if (this._userGesturesInProgress == 0)
foreach (var observer in this.widget.observers)
}
}
HashSet<int> _activePointers = new HashSet<int>();
void _handlePointerDown(PointerDownEvent evt)
{
_activePointers.Add(evt.pointer);
void _handlePointerDown(PointerDownEvent evt) {
this._activePointers.Add(evt.pointer);
void _handlePointerUpOrCancel(PointerEvent evt)
{
_activePointers.Remove(evt.pointer);
void _handlePointerUpOrCancel(PointerEvent evt) {
this._activePointers.Remove(evt.pointer);
void _cancelActivePointers()
{
void _cancelActivePointers() {
if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.idle)
{
if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.idle) {
RenderAbsorbPointer absorber = (RenderAbsorbPointer) _overlayKey.currentContext?
var absorber = (RenderAbsorbPointer) this._overlayKey.currentContext?
setState(() =>
{
if (absorber != null)
{
absorber.absorbing = true;
}
this.setState(() => {
if (absorber != null) absorber.absorbing = true;
foreach (var activePointer in _activePointers)
{
WidgetsBinding.instance.cancelPointer(activePointer);
}
foreach (var activePointer in this._activePointers) WidgetsBinding.instance.cancelPointer(activePointer);
public override Widget build(BuildContext context)
{
D.assert(!_debugLocked);
D.assert(_history.isNotEmpty());
public override Widget build(BuildContext context) {
D.assert(!this._debugLocked);
D.assert(this._history.isNotEmpty());
onPointerDown: _handlePointerDown,
onPointerUp: _handlePointerUpOrCancel,
onPointerCancel: _handlePointerUpOrCancel,
onPointerDown: this._handlePointerDown,
onPointerUp: this._handlePointerUpOrCancel,
onPointerCancel: this._handlePointerUpOrCancel,
node: focusScopeNode,
this.focusScopeNode,
key: _overlayKey,
initialEntries: _initialOverlayEntries
this._overlayKey,
this._initialOverlayEntries
)
)
)

157
Runtime/widgets/overlay.cs


public OverlayEntry(WidgetBuilder builder = null, bool opaque = false, bool maintainState = false)
{
D.assert(builder != null);
_opaque = opaque;
_maintainState = maintainState;
this._opaque = opaque;
this._maintainState = maintainState;
this.builder = builder;
}
public readonly WidgetBuilder builder;

public bool opaque
{
get { return _opaque; }
get { return this._opaque; }
if (_opaque == value)
if (this._opaque == value)
_opaque = value;
D.assert(_overlay != null);
_overlay._didChangeEntryOpacity();
this._opaque = value;
D.assert(this._overlay != null);
this._overlay._didChangeEntryOpacity();
}
}

{
get { return _maintainState; }
get { return this._maintainState; }
if (_maintainState == value)
if (this._maintainState == value)
_maintainState = value;
D.assert(_overlay != null);
_overlay._didChangeEntryOpacity();
this._maintainState = value;
D.assert(this._overlay != null);
this._overlay._didChangeEntryOpacity();
}
}

public void remove()
{
D.assert(_overlay != null);
OverlayState overlay = _overlay;
_overlay = null;
D.assert(this._overlay != null);
OverlayState overlay = this._overlay;
this._overlay = null;
if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks)
{
SchedulerBinding.instance.addPostFrameCallback((duration) => { overlay._remove(this); });

public void markNeedsBuild()
{
_key.currentState?._markNeedsBuild();
this._key.currentState?._markNeedsBuild();
return $"{Diagnostics.describeIdentity(this)}(opaque: {opaque}; maintainState: {maintainState})";
return $"{Diagnostics.describeIdentity(this)}(opaque: {this.opaque}; maintainState: {this.maintainState})";
}
}

internal _OverlayEntry(OverlayEntry entry) : base(key: entry._key)
{
D.assert(entry != null);
this.entry = entry;
}
public readonly OverlayEntry entry;

{
public override Widget build(BuildContext context)
{
return widget.entry.builder(context);
return this.widget.entry.builder(context);
setState(() =>
this.setState(() =>
{
/* the state that changed is in the builder */
});

public override void initState()
{
base.initState();
insertAll(widget.initialEntries);
this.insertAll(this.widget.initialEntries);
D.assert(above == null || (above._overlay == this && _entries.Contains(above)));
D.assert(above == null || (above._overlay == this && this._entries.Contains(above)));
setState(() =>
this.setState(() =>
int index = above == null ? _entries.Count : _entries.IndexOf(above) + 1;
_entries.Insert(index, entry);
int index = above == null ? this._entries.Count : this._entries.IndexOf(above) + 1;
this._entries.Insert(index, entry);
D.assert(above == null || (above._overlay == this && _entries.Contains(above)));
D.assert(above == null || (above._overlay == this && this._entries.Contains(above)));
if (entries.isEmpty())
return;
foreach (OverlayEntry entry in entries)

}
setState(() =>
this.setState(() =>
int index = above == null ? _entries.Count : _entries.IndexOf(above) + 1;
_entries.InsertRange(index, entries);
int index = above == null ? this._entries.Count : this._entries.IndexOf(above) + 1;
this._entries.InsertRange(index, entries);
if (mounted)
if (this.mounted)
_entries.Remove(entry);
setState(() =>
this._entries.Remove(entry);
this.setState(() =>
{
/* entry was removed */
});

public bool debugIsVisible(OverlayEntry entry)
{
bool result = false;
D.assert(_entries.Contains(entry));
D.assert(this._entries.Contains(entry));
for (int i = _entries.Count - 1; i > 0; i -= 1)
for (int i = this._entries.Count - 1; i > 0; i -= 1)
OverlayEntry candidate = _entries[i];
OverlayEntry candidate = this._entries[i];
if (candidate == entry)
{
result = true;

internal void _didChangeEntryOpacity()
{
setState(() => { });
this.setState(() => { });
}
public override Widget build(BuildContext context)

var onstage = true;
for (var i = _entries.Count - 1; i >= 0; i -= 1)
for (var i = this._entries.Count - 1; i >= 0; i -= 1)
var entry = _entries[i];
var entry = this._entries[i];
if (onstage)
{
onstageChildren.Add(new _OverlayEntry(entry));

protected override void insertChildRenderObject(RenderObject child, object slot)
{
D.assert(renderObject.debugValidateChild(child));
D.assert(this.renderObject.debugValidateChild(child));
renderObject.child = (RenderStack) child;
this.renderObject.child = (RenderStack) child;
renderObject.insert((RenderBox) child, after: (RenderBox) ((Element) slot)?.renderObject);
this.renderObject.insert((RenderBox) child, after: (RenderBox) ((Element) slot)?.renderObject);
}
}

{
renderObject.remove((RenderBox) child);
this.renderObject.remove((RenderBox) child);
renderObject.child = (RenderStack) child;
this.renderObject.child = (RenderStack) child;
if (renderObject.child == child)
if (this.renderObject.child == child)
renderObject.child = null;
renderObject.insert((RenderBox) child, after: (RenderBox) ((Element) slot)?.renderObject);
this.renderObject.child = null;
this.renderObject.insert((RenderBox) child, after: (RenderBox) ((Element) slot)?.renderObject);
renderObject.move((RenderBox) child, after: (RenderBox) ((Element) slot)?.renderObject);
this.renderObject.move((RenderBox) child, after: (RenderBox) ((Element) slot)?.renderObject);
}
}
}

if (renderObject.child == child)
if (this.renderObject.child == child)
renderObject.child = null;
this.renderObject.child = null;
renderObject.remove((RenderBox) child);
this.renderObject.remove((RenderBox) child);
if (_onstage != null)
visitor(_onstage);
foreach (var child in _offstage)
if (this._onstage != null)
visitor(this._onstage);
foreach (var child in this._offstage)
if (!_forgottenOffstageChildren.Contains(child))
if (!this._forgottenOffstageChildren.Contains(child))
visitor(child);
}
}

if (_onstage != null)
visitor(_onstage);
if (this._onstage != null)
visitor(this._onstage);
if (child == _onstage)
if (child == this._onstage)
_onstage = null;
this._onstage = null;
D.assert(_offstage.Contains(child));
D.assert(!_forgottenOffstageChildren.Contains(child));
_forgottenOffstageChildren.Add(child);
D.assert(this._offstage.Contains(child));
D.assert(!this._forgottenOffstageChildren.Contains(child));
this._forgottenOffstageChildren.Add(child);
}
}

_onstage = updateChild(_onstage, widget.onstage, _onstageSlot);
_offstage = new List<Element>(widget.offstage.Count);
this._onstage = this.updateChild(this._onstage, this.widget.onstage, _onstageSlot);
this._offstage = new List<Element>(this.widget.offstage.Count);
for (int i = 0; i < _offstage.Count; i += 1)
for (int i = 0; i < this._offstage.Count; i += 1)
var newChild = inflateWidget(widget.offstage[i], previousChild);
_offstage[i] = newChild;
var newChild = this.inflateWidget(this.widget.offstage[i], previousChild);
this._offstage[i] = newChild;
previousChild = newChild;
}
}

base.update(newWidget);
D.assert(Equals(widget, newWidget));
_onstage = updateChild(_onstage, widget.onstage, _onstageSlot);
_offstage = updateChildren(_offstage, widget.offstage, forgottenChildren: _forgottenOffstageChildren);
_forgottenOffstageChildren.Clear();
D.assert(Equals(this.widget, newWidget));
this._onstage = this.updateChild(this._onstage, this.widget.onstage, _onstageSlot);
this._offstage = this.updateChildren(this._offstage, this.widget.offstage, forgottenChildren: this._forgottenOffstageChildren);
this._forgottenOffstageChildren.Clear();
}
}

public override void redepthChildren()
{
if (child != null)
redepthChild(child);
if (this.child != null) this.redepthChild(this.child);
if (child != null)
visitor(child);
if (this.child != null)
visitor(this.child);
base.visitChildren(visitor);
}

if (child != null)
children.Add(child.toDiagnosticsNode(name: "onstage"));
if (this.child != null)
children.Add(this.child.toDiagnosticsNode(name: "onstage"));
if (firstChild != null)
if (this.firstChild != null)
var child = firstChild;
var child = this.firstChild;
int count = 1;
while (true)

style: DiagnosticsTreeStyle.offstage
)
);
if (child == lastChild)
if (child == this.lastChild)
break;
var childParentData = (StackParentData) child.parentData;
child = childParentData.nextSibling;

6
Runtime/widgets/page_storage.cs


public class PageStorage : StatelessWidget {
public PageStorage(
Key key,
PageStorageBucket bucket,
Widget child
Key key = null,
PageStorageBucket bucket = null,
Widget child = null
) : base(key: key) {
D.assert(bucket != null);
this.bucket = bucket;

9
Runtime/widgets/widget_inspector.cs


using System;
using System.Collections.Generic;
using System.Linq;
using RSG;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.rendering;

}
public void didChangeLocales(List<Locale> locale) {
}
public IPromise<bool> didPopRoute() {
return Promise<bool>.Resolved(false);
}
public IPromise<bool> didPushRoute(string route) {
return Promise<bool>.Resolved(false);
}
void _selectionChangedCallback() {

7
Samples/UIWidgetSample/TextInputCanvas.cs


}
class _TextInputSampleState : State<TextInputSample> {
public class ToDoItem {
public int id;
public string content;
}
int nextId = 0;
TextEditingController titleController = new TextEditingController("");
TextEditingController descController = new TextEditingController("");
FocusNode _titleFocusNode;

48
Runtime/widgets/modal_barrier.cs


using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.widgets {
public class ModalBarrier : StatelessWidget {
public readonly Color color;
public readonly bool dismissible;
public ModalBarrier(Key key = null, Color color = null, bool dismissible = true) : base(key) {
this.color = color;
this.dismissible = dismissible;
}
public override Widget build(BuildContext context) {
//retur
return new GestureDetector(
onTapDown: details => {
if (this.dismissible)
Navigator.maybePop(context);
},
behavior: HitTestBehavior.opaque,
child: new ConstrainedBox(
constraints: BoxConstraints.expand(),
child: this.color == null ? null : new DecoratedBox(decoration: new BoxDecoration(this.color))
)
);
}
}
public class AnimatedModalBarrier : AnimatedWidget {
public readonly bool dismissible;
public AnimatedModalBarrier(Key key = null, Animation<Color> color = null,
bool dismissible = true) : base(key, color) {
this.dismissible = dismissible;
}
public Animation<Color> color => (Animation<Color>)this.listenable;
protected internal override Widget build(BuildContext context) {
return new ModalBarrier(color: this.color?.value, dismissible: this.dismissible);
}
}
}

11
Runtime/widgets/modal_barrier.cs.meta


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

89
Runtime/widgets/pages.cs


using System;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.widgets {
public abstract class PageRoute : ModalRoute {
public readonly bool fullscreenDialog;
public PageRoute(RouteSettings settings, bool fullscreenDialog = false) : base(settings) {
this.fullscreenDialog = fullscreenDialog;
}
public override bool opaque => false;
public override bool barrierDismissible => false;
public override bool canTransitionTo(TransitionRoute nextRoute) {
return nextRoute is PageRoute;
}
public override bool canTransitionFrom(TransitionRoute previousRoute) {
return previousRoute is PageRoute;
}
public override AnimationController createAnimationController() {
var controller = base.createAnimationController();
if (this.settings.isInitialRoute)
controller.setValue(1.0);
return controller;
}
}
public class PageRouteBuilder : PageRoute {
public readonly RoutePageBuilder pageBuilder;
public readonly RouteTransitionsBuilder transitionsBuilder;
public PageRouteBuilder(
RouteSettings settings = null,
RoutePageBuilder pageBuilder = null,
RouteTransitionsBuilder transitionsBuilder = null,
TimeSpan? transitionDuration = null,
bool opaque = true,
bool barrierDismissible = false,
Color barrierColor = null,
string barrierLabel = null,
bool maintainState = true
) : base(settings) {
D.assert(pageBuilder != null);
this.opaque = opaque;
this.pageBuilder = pageBuilder;
this.transitionsBuilder = transitionsBuilder??this._defaultTransitionsBuilder;
this.transitionDuration = transitionDuration ?? TimeSpan.FromMilliseconds(300);
this.barrierColor = barrierColor;
this.maintainState = maintainState;
this.barrierLabel = barrierLabel;
this.barrierDismissible = barrierDismissible;
}
public override TimeSpan transitionDuration { get; }
public override bool opaque { get; }
public override bool barrierDismissible { get; }
public override Color barrierColor { get; }
public override string barrierLabel { get; }
public override bool maintainState { get; }
Widget _defaultTransitionsBuilder(BuildContext context, Animation<double>
animation, Animation<double> secondaryAnimation, Widget child) {
return child;
}
public override Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return this.pageBuilder(context, animation, secondaryAnimation);
}
public override Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return this.transitionsBuilder(context, animation, secondaryAnimation, child);
}
}
}

11
Runtime/widgets/pages.cs.meta


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

757
Runtime/widgets/routes.cs


using System;
using System.Collections.Generic;
using System.Linq;
using RSG;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.widgets {
public abstract class OverlayRoute : Route {
readonly List<OverlayEntry> _overlayEntries = new List<OverlayEntry>();
public OverlayRoute(
RouteSettings settings = null
) : base(settings) {
}
public override List<OverlayEntry> overlayEntries => this._overlayEntries;
protected virtual bool finishedWhenPopped => true;
public abstract ICollection<OverlayEntry> createOverlayEntries();
protected internal override void install(OverlayEntry insertionPoint) {
D.assert(this._overlayEntries.isEmpty());
this._overlayEntries.AddRange(this.createOverlayEntries());
this.navigator.overlay?.insertAll(this._overlayEntries, insertionPoint);
base.install(insertionPoint);
}
protected internal override bool didPop(object result) {
var returnValue = base.didPop(result);
D.assert(returnValue);
if (this.finishedWhenPopped) this.navigator.finalizeRoute(this);
return returnValue;
}
protected internal override void dispose() {
foreach (var entry in this._overlayEntries) entry.remove();
this._overlayEntries.Clear();
base.dispose();
}
}
public abstract class TransitionRoute : OverlayRoute {
public TransitionRoute(
RouteSettings settings = null
) : base(settings) {
}
public IPromise<object> completed {
get { return this._transitionCompleter; }
}
internal readonly Promise<object> _transitionCompleter = new Promise<object>();
public virtual TimeSpan transitionDuration { get; }
public virtual bool opaque { get; }
protected override bool finishedWhenPopped => this.controller.status == AnimationStatus.dismissed;
public virtual Animation<double> animation {
get { return this._animation; }
}
internal Animation<double> _animation;
public AnimationController controller {
get { return this._controller; }
}
internal AnimationController _controller;
public virtual AnimationController createAnimationController() {
D.assert(this._transitionCompleter.CurState == PromiseState.Pending,
$"Cannot reuse a {this.GetType()} after disposing it.");
TimeSpan duration = this.transitionDuration;
D.assert(duration >= TimeSpan.Zero);
return new AnimationController(
duration: duration,
debugLabel: this.debugLabel,
vsync: this.navigator
);
}
public virtual Animation<double> createAnimation() {
D.assert(this._transitionCompleter.CurState == PromiseState.Pending,
$"Cannot reuse a {this.GetType()} after disposing it.");
D.assert(this._controller != null);
return this._controller.view;
}
object _result;
internal void _handleStatusChanged(AnimationStatus status) {
switch (status) {
case AnimationStatus.completed:
if (this.overlayEntries.isNotEmpty())
this.overlayEntries.first().opaque = this.opaque;
break;
case AnimationStatus.forward:
case AnimationStatus.reverse:
if (this.overlayEntries.isNotEmpty())
this.overlayEntries.first().opaque = false;
break;
case AnimationStatus.dismissed:
D.assert(!this.overlayEntries.first().opaque);
// We might still be the current route if a subclass is controlling the
// the transition and hits the dismissed status. For example, the iOS
// back gesture drives this animation to the dismissed status before
// popping the navigator.
if (!this.isCurrent) {
this.navigator.finalizeRoute(this);
D.assert(this.overlayEntries.isEmpty());
}
break;
}
this.changedInternalState();
}
public virtual Animation<double> secondaryAnimation {
get { return this._secondaryAnimation; }
}
readonly ProxyAnimation _secondaryAnimation = new ProxyAnimation(Animations.kAlwaysDismissedAnimation);
protected internal override void install(OverlayEntry insertionPoint) {
D.assert(!this._transitionCompleter.isCompleted, $"Cannot install a {this.GetType()} after disposing it.");
this._controller = this.createAnimationController();
D.assert(this._controller != null, $"{this.GetType()}.createAnimationController() returned null.");
this._animation = this.createAnimation();
D.assert(this._animation != null, $"{this.GetType()}.createAnimation() returned null.");
base.install(insertionPoint);
}
protected internal override TickerFuture didPush() {
D.assert(this._controller != null,
$"{this.GetType()}.didPush called before calling install() or after calling dispose().");
D.assert(!this._transitionCompleter.isCompleted, $"Cannot reuse a {this.GetType()} after disposing it.");
this._animation.addStatusListener(this._handleStatusChanged);
return this._controller.forward();
}
protected internal override void didReplace(Route oldRoute) {
D.assert(this._controller != null,
$"{this.GetType()}.didReplace called before calling install() or after calling dispose().");
D.assert(!this._transitionCompleter.isCompleted, $"Cannot reuse a {this.GetType()} after disposing it.");
if (oldRoute is TransitionRoute route)
this._controller.setValue(route._controller.value);
this._animation.addStatusListener(this._handleStatusChanged);
base.didReplace(oldRoute);
}
protected internal override bool didPop(object result) {
D.assert(this._controller != null,
$"{this.GetType()}.didPop called before calling install() or after calling dispose().");
D.assert(!this._transitionCompleter.isCompleted, $"Cannot reuse a {this.GetType()} after disposing it.");
this._result = result;
this._controller.reverse();
return base.didPop(result);
}
protected internal override void didPopNext(Route nextRoute) {
D.assert(this._controller != null,
$"{this.GetType()}.didPopNext called before calling install() or after calling dispose().");
D.assert(!this._transitionCompleter.isCompleted, $"Cannot reuse a {this.GetType()} after disposing it.");
this._updateSecondaryAnimation(nextRoute);
base.didPopNext(nextRoute);
}
protected internal override void didChangeNext(Route nextRoute) {
D.assert(this._controller != null,
$"{this.GetType()}.didChangeNext called before calling install() or after calling dispose().");
D.assert(!this._transitionCompleter.isCompleted, $"Cannot reuse a {this.GetType()} after disposing it.");
this._updateSecondaryAnimation(nextRoute);
base.didChangeNext(nextRoute);
}
void _updateSecondaryAnimation(Route nextRoute) {
if (nextRoute is TransitionRoute && this.canTransitionTo((TransitionRoute) nextRoute) &&
((TransitionRoute) nextRoute).canTransitionFrom(this)) {
Animation<double> current = this._secondaryAnimation.parent;
if (current != null) {
if (current is TrainHoppingAnimation) {
TrainHoppingAnimation newAnimation = null;
newAnimation = new TrainHoppingAnimation(
((TrainHoppingAnimation) current).currentTrain,
((TransitionRoute) nextRoute)._animation,
onSwitchedTrain: () => {
D.assert(this._secondaryAnimation.parent == newAnimation);
D.assert(newAnimation.currentTrain == ((TransitionRoute) nextRoute)._animation);
this._secondaryAnimation.parent = newAnimation.currentTrain;
newAnimation.dispose();
}
);
this._secondaryAnimation.parent = newAnimation;
((TrainHoppingAnimation) current).dispose();
}
else {
this._secondaryAnimation.parent =
new TrainHoppingAnimation(current, ((TransitionRoute) nextRoute)._animation);
}
}
else {
this._secondaryAnimation.parent = ((TransitionRoute) nextRoute)._animation;
}
}
else {
this._secondaryAnimation.parent = Animations.kAlwaysDismissedAnimation;
}
}
public virtual bool canTransitionTo(TransitionRoute nextRoute) => true;
public virtual bool canTransitionFrom(TransitionRoute previousRoute) => true;
protected internal override void dispose() {
D.assert(!this._transitionCompleter.isCompleted, $"Cannot dispose a {this.GetType()} twice.");
this._controller?.dispose();
this._transitionCompleter.Resolve(this._result);
base.dispose();
}
public string debugLabel => $"{this.GetType()}";
public override string ToString() {
return $"{this.GetType()}(animation: {this._controller}";
}
}
public class LocalHistoryEntry {
public LocalHistoryEntry(VoidCallback onRemove = null) {
this.onRemove = onRemove;
}
public readonly VoidCallback onRemove;
internal LocalHistoryRoute _owner;
public void remove() {
this._owner.removeLocalHistoryEntry(this);
D.assert(this._owner == null);
}
internal void _notifyRemoved() {
this.onRemove?.Invoke();
}
}
public interface LocalHistoryRoute {
void addLocalHistoryEntry(LocalHistoryEntry entry);
void removeLocalHistoryEntry(LocalHistoryEntry entry);
Route route { get; }
}
// todo make it to mixin
public abstract class LocalHistoryRouteTransitionRoute : TransitionRoute, LocalHistoryRoute {
List<LocalHistoryEntry> _localHistory;
protected LocalHistoryRouteTransitionRoute(RouteSettings settings = null) : base(settings: settings) {
}
public void addLocalHistoryEntry(LocalHistoryEntry entry) {
D.assert(entry._owner == null);
entry._owner = this;
this._localHistory = this._localHistory ?? new List<LocalHistoryEntry>();
var wasEmpty = this._localHistory.isEmpty();
this._localHistory.Add(entry);
if (wasEmpty)
this.changedInternalState();
}
public void removeLocalHistoryEntry(LocalHistoryEntry entry) {
D.assert(entry != null);
D.assert(entry._owner == this);
D.assert(this._localHistory.Contains(entry));
this._localHistory.Remove(entry);
entry._owner = null;
entry._notifyRemoved();
if (this._localHistory.isEmpty()) this.changedInternalState();
}
public override IPromise<RoutePopDisposition> willPop() {
if (this.willHandlePopInternally)
return Promise<RoutePopDisposition>.Resolved(RoutePopDisposition.pop);
return base.willPop();
}
protected internal override bool didPop(object result) {
if (this._localHistory != null && this._localHistory.isNotEmpty()) {
var entry = this._localHistory.removeLast();
D.assert(entry._owner == this);
entry._owner = null;
entry._notifyRemoved();
if (this._localHistory.isEmpty())
this.changedInternalState();
return false;
}
return base.didPop(result);
}
public override bool willHandlePopInternally => this._localHistory != null && this._localHistory.isNotEmpty();
public Route route => this;
}
public class _ModalScopeStatus : InheritedWidget {
public _ModalScopeStatus(Key key = null, bool isCurrent = false,
bool canPop = false, Route route = null, Widget child = null) : base(key: key, child: child) {
D.assert(route != null);
D.assert(child != null);
this.isCurrent = isCurrent;
this.canPop = canPop;
this.route = route;
}
public readonly bool isCurrent;
public readonly bool canPop;
public readonly Route route;
public override bool updateShouldNotify(InheritedWidget oldWidget) {
return this.isCurrent != ((_ModalScopeStatus) oldWidget).isCurrent ||
this.canPop != ((_ModalScopeStatus) oldWidget).canPop ||
this.route != ((_ModalScopeStatus) oldWidget).route;
}
public override void debugFillProperties(DiagnosticPropertiesBuilder description) {
base.debugFillProperties(description);
description.add(new FlagProperty("isCurrent", value: this.isCurrent, ifTrue: "active", ifFalse: "inactive"));
description.add(new FlagProperty("canPop", value: this.canPop, ifTrue: "can pop"));
}
}
public class _ModalScope : StatefulWidget {
public _ModalScope(Key key = null, ModalRoute route = null) : base(key) {
this.route = route;
}
public readonly ModalRoute route;
public override State createState() {
return new _ModalScopeState();
}
}
public class _ModalScopeState : State<_ModalScope> {
Widget _page;
Listenable _listenable;
public override void initState() {
base.initState();
var animations = new List<Listenable> { };
if (this.widget.route.animation != null)
animations.Add(this.widget.route.animation);
if (this.widget.route.secondaryAnimation != null)
animations.Add(this.widget.route.secondaryAnimation);
this._listenable = ListenableUtils.merge(animations);
}
public override void didUpdateWidget(StatefulWidget oldWidget) {
base.didUpdateWidget(oldWidget);
D.assert(this.widget.route == ((_ModalScope) oldWidget).route);
}
public override void didChangeDependencies() {
base.didChangeDependencies();
this._page = null;
}
internal void _forceRebuildPage() {
this.setState(() => { this._page = null; });
}
internal void _routeSetState(VoidCallback fn) {
this.setState(fn);
}
public override Widget build(BuildContext context) {
this._page = this._page ?? new RepaintBoundary(
key: this.widget.route._subtreeKey, // immutable
child: new Builder(
builder: (BuildContext _context) => this.widget.route.buildPage(
_context,
this.widget.route.animation,
this.widget.route.secondaryAnimation
))
);
return new _ModalScopeStatus(
route: this.widget.route,
isCurrent: this.widget.route.isCurrent,
canPop: this.widget.route.canPop,
child: new Offstage(
offstage: this.widget.route.offstage,
child: new PageStorage(
bucket: this.widget.route._storageBucket,
child: new FocusScope(
node: this.widget.route.focusScopeNode,
child: new RepaintBoundary(
child: new AnimatedBuilder(
animation: this._listenable, // immutable
builder: (BuildContext _context, Widget child) =>
this.widget.route.buildTransitions(
_context,
this.widget.route.animation,
this.widget.route.secondaryAnimation,
new IgnorePointer(
ignoring: this.widget.route.animation?.status ==
AnimationStatus.reverse,
child: child
)
),
child: this._page
)
)
)
)
)
);
}
}
public abstract class ModalRoute : LocalHistoryRouteTransitionRoute {
protected ModalRoute(RouteSettings settings) : base(settings) {
}
public static Color _kTransparent = new Color(0x00000000);
public static ModalRoute of(BuildContext context) {
_ModalScopeStatus widget =
(_ModalScopeStatus) context.inheritFromWidgetOfExactType(typeof(_ModalScopeStatus));
return (ModalRoute) widget?.route;
}
protected virtual void setState(VoidCallback fn) {
if (this._scopeKey.currentState != null) {
this._scopeKey.currentState._routeSetState(fn);
}
else {
// The route isn't currently visible, so we don't have to call its setState
// method, but we do still need to call the fn callback, otherwise the state
// in the route won't be updated!
fn();
}
}
public RoutePredicate withName(String name) {
return (Route route) => !route.willHandlePopInternally
&& route is ModalRoute
&& route.settings.name == name;
}
public abstract Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation);
public virtual Widget buildTransitions(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child
) {
return child;
}
public readonly FocusScopeNode focusScopeNode = new FocusScopeNode();
protected internal override void install(OverlayEntry insertionPoint) {
base.install(insertionPoint);
this._animationProxy = new ProxyAnimation(base.animation);
this._secondaryAnimationProxy = new ProxyAnimation(base.secondaryAnimation);
}
protected internal override TickerFuture didPush() {
this.navigator.focusScopeNode.setFirstFocus(this.focusScopeNode);
return base.didPush();
}
protected internal override void dispose() {
this.focusScopeNode.detach();
base.dispose();
}
public virtual bool barrierDismissible { get; }
public virtual Color barrierColor { get; }
public virtual string barrierLabel { get; }
public virtual bool maintainState { get; }
public bool offstage {
get => this._offstage;
set {
if (this._offstage == value)
return;
this.setState(() => { this._offstage = value; });
this._animationProxy.parent = this._offstage ? Animations.kAlwaysCompleteAnimation : base.animation;
this._secondaryAnimationProxy.parent = this._offstage ? Animations.kAlwaysDismissedAnimation : base.secondaryAnimation;
}
}
bool _offstage = false;
public BuildContext subtreeContext => this._subtreeKey.currentContext;
public override Animation<double> animation => this._animationProxy;
ProxyAnimation _animationProxy;
public override Animation<double> secondaryAnimation => this._secondaryAnimationProxy;
ProxyAnimation _secondaryAnimationProxy;
readonly List<WillPopCallback> _willPopCallbacks = new List<WillPopCallback>();
public override IPromise<RoutePopDisposition> willPop() {
_ModalScopeState scope = this._scopeKey.currentState;
D.assert(scope != null);
var callbacks = new List<WillPopCallback>(this._willPopCallbacks);
Promise<RoutePopDisposition> result = new Promise<RoutePopDisposition>();
Action<int> fn = null;
fn = (int index) => {
if (index < callbacks.Count) {
callbacks[index]().Then((pop) => {
if (!pop) {
result.Resolve(RoutePopDisposition.doNotPop);
}
else {
fn(index + 1);
}
});
}
else {
base.willPop().Then((pop) => result.Resolve(pop));
}
};
fn(0);
return result;
}
public void addScopedWillPopCallback(WillPopCallback callback) {
D.assert(this._scopeKey.currentState != null,
"Tried to add a willPop callback to a route that is not currently in the tree.");
this._willPopCallbacks.Add(callback);
}
public void removeScopedWillPopCallback(WillPopCallback callback) {
D.assert(this._scopeKey.currentState != null,
"Tried to remove a willPop callback from a route that is not currently in the tree.");
this._willPopCallbacks.Remove(callback);
}
protected bool hasScopedWillPopCallback => this._willPopCallbacks.isNotEmpty();
protected internal override void didChangePrevious(Route previousRoute) {
base.didChangePrevious(previousRoute);
this.changedInternalState();
}
protected internal override void changedInternalState() {
base.changedInternalState();
this.setState(() => { });
this._modalBarrier.markNeedsBuild();
}
protected internal override void changedExternalState() {
base.changedExternalState();
this._scopeKey.currentState?._forceRebuildPage();
}
public bool canPop => !this.isFirst || this.willHandlePopInternally;
readonly GlobalKey<_ModalScopeState> _scopeKey = new LabeledGlobalKey<_ModalScopeState>();
internal readonly GlobalKey _subtreeKey = new LabeledGlobalKey<_ModalScopeState>();
internal readonly PageStorageBucket _storageBucket = new PageStorageBucket();
static readonly Animatable<double> _easeCurveTween = new CurveTween(curve: Curves.ease);
OverlayEntry _modalBarrier;
Widget _buildModalBarrier(BuildContext context) {
Widget barrier;
if (this.barrierColor != null && !this.offstage) {
// changedInternalState is called if these update
D.assert(this.barrierColor != _kTransparent);
Animation<Color> color =
new ColorTween(
begin: _kTransparent,
end: this.barrierColor // changedInternalState is called if this updates
).chain(_easeCurveTween).animate(this.animation);
barrier = new AnimatedModalBarrier(
color: color,
dismissible: this.barrierDismissible
);
}
else {
barrier = new ModalBarrier(
dismissible: this.barrierDismissible
);
}
return new IgnorePointer(
ignoring: this.animation.status == AnimationStatus.reverse || this.animation.status == AnimationStatus.dismissed,
child: barrier
);
}
Widget _modalScopeCache;
Widget _buildModalScope(BuildContext context) {
return this._modalScopeCache = this._modalScopeCache ?? new _ModalScope(
key: this._scopeKey,
route: this
// _ModalScope calls buildTransitions() and buildChild(), defined above
);
}
public override ICollection<OverlayEntry> createOverlayEntries() {
this._modalBarrier = new OverlayEntry(builder: this._buildModalBarrier);
var content = new OverlayEntry(
builder: this._buildModalScope, maintainState: this.maintainState
);
return new List<OverlayEntry> {this._modalBarrier, content};
}
public override string ToString() {
return $"{this.GetType()}({this.settings}, animation: {this._animation})";
}
}
abstract class PopupRoute : ModalRoute {
protected PopupRoute(
RouteSettings settings = null
) : base(settings: settings) {
}
public override bool opaque => false;
public override bool maintainState => true;
}
public class RouteObserve<R> : NavigatorObserver where R : Route {
readonly Dictionary<R, HashSet<RouteAware>> _listeners = new Dictionary<R, HashSet<RouteAware>>();
public void subscribe(RouteAware routeAware, R route) {
D.assert(routeAware != null);
D.assert(route != null);
HashSet<RouteAware> subscribers = this._listeners.putIfAbsent(route, () => new HashSet<RouteAware>());
if (subscribers.Add(routeAware)) {
routeAware.didPush();
}
}
public void unsubscribe(RouteAware routeAware) {
D.assert(routeAware != null);
foreach (R route in this._listeners.Keys) {
HashSet<RouteAware> subscribers = this._listeners[route];
subscribers?.Remove(routeAware);
}
}
public override void didPop(Route route, Route previousRoute) {
if (route is R && previousRoute is R) {
var previousSubscribers = this._listeners.getOrDefault((R) previousRoute);
if (previousSubscribers != null) {
foreach (RouteAware routeAware in previousSubscribers) {
routeAware.didPopNext();
}
}
var subscribers = this._listeners.getOrDefault((R) route);
if (subscribers != null) {
foreach (RouteAware routeAware in subscribers) {
routeAware.didPop();
}
}
}
}
public override void didPush(Route route, Route previousRoute) {
if (route is R && previousRoute is R) {
var previousSubscribers = this._listeners.getOrDefault((R) previousRoute);
if (previousSubscribers != null) {
foreach (RouteAware routeAware in previousSubscribers) {
routeAware.didPushNext();
}
}
}
}
}
public interface RouteAware {
void didPopNext();
void didPush();
void didPop();
void didPushNext();
}
internal class _DialogRoute : PopupRoute {
internal _DialogRoute(RoutePageBuilder pageBuilder = null, bool barrierDismissible = true,
string barrierLabel = null,
Color barrierColor = null,
TimeSpan? transitionDuration = null,
RouteTransitionsBuilder transitionBuilder = null,
RouteSettings setting = null) : base(settings: setting) {
this._pageBuilder = pageBuilder;
this.barrierDismissible = barrierDismissible;
this.barrierLabel = barrierLabel;
this.barrierColor = barrierColor ?? new Color(0x80000000);
this.transitionDuration = transitionDuration ?? TimeSpan.FromMilliseconds(200);
this._transitionBuilder = transitionBuilder;
}
readonly RoutePageBuilder _pageBuilder;
public override bool barrierDismissible { get; }
public override string barrierLabel { get; }
public override Color barrierColor { get; }
public override TimeSpan transitionDuration { get; }
readonly RouteTransitionsBuilder _transitionBuilder;
public override Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return this._pageBuilder(context, animation, secondaryAnimation);
}
}
public delegate Widget RoutePageBuilder(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation);
public delegate Widget RouteTransitionsBuilder(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child);
}

11
Runtime/widgets/routes.cs.meta


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

133
Runtime/widgets/transitions.cs


using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.widgets {
public abstract class AnimatedWidget : StatefulWidget {
public readonly Listenable listenable;
protected AnimatedWidget(Key key = null, Listenable listenable = null) : base(key) {
D.assert(listenable != null);
this.listenable = listenable;
}
protected internal abstract Widget build(BuildContext context);
public override State createState() {
return new _AnimatedState();
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<Listenable>("animation", this.listenable));
}
}
public class _AnimatedState : State<AnimatedWidget> {
public override void initState() {
base.initState();
this.widget.listenable.addListener(this._handleChange);
}
public override void didUpdateWidget(StatefulWidget oldWidget) {
base.didUpdateWidget(oldWidget);
if (this.widget.listenable != ((AnimatedWidget) oldWidget).listenable) {
((AnimatedWidget) oldWidget).listenable.removeListener(this._handleChange);
this.widget.listenable.addListener(this._handleChange);
}
}
public override void dispose() {
this.widget.listenable.removeListener(this._handleChange);
base.dispose();
}
void _handleChange() {
this.setState(() => {
// The listenable's state is our build state, and it changed already.
});
}
public override Widget build(BuildContext context) {
return this.widget.build(context);
}
}
public class SlideTransition : AnimatedWidget {
public SlideTransition(Key key = null,
Animation<Offset> position = null,
bool transformHitTests = true,
TextDirection? textDirection = null,
Widget child = null): base(key: key, listenable: position) {
D.assert(position != null);
this.transformHitTests = transformHitTests;
this.textDirection = textDirection;
this.child = child;
}
public Animation<Offset> position => (Animation<Offset>)this.listenable;
public readonly TextDirection? textDirection;
public readonly bool transformHitTests;
public readonly Widget child;
protected internal override Widget build(BuildContext context) {
var offset = this.position.value;
if (this.textDirection == TextDirection.rtl)
offset = new Offset(-offset.dx, offset.dy);
return new FractionalTranslation(
translation: offset,
transformHitTests: this.transformHitTests,
child: this.child
);
}
}
public class FadeTransition : SingleChildRenderObjectWidget {
public FadeTransition(Key key = null, Animation<double> opacity = null,
Widget child = null): base(key: key, child: child) {
this.opacity = opacity;
}
public readonly Animation<double> opacity;
public override RenderObject createRenderObject(BuildContext context) {
return new RenderAnimatedOpacity(
opacity: this.opacity
);
}
public override void updateRenderObject(BuildContext context, RenderObject renderObject) {
((RenderAnimatedOpacity) renderObject).opacity = this.opacity;
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<Animation<double>>("opacity", this.opacity));
}
}
public class AnimatedBuilder : AnimatedWidget {
public readonly TransitionBuilder builder;
public readonly Widget child;
public AnimatedBuilder(Key key = null, Listenable animation = null, TransitionBuilder builder = null,
Widget child = null) :
base(key, animation) {
D.assert(builder != null);
this.builder = builder;
this.child = child;
}
protected internal override Widget build(BuildContext context) {
return this.builder(context, this.child);
}
}
}

11
Runtime/widgets/transitions.cs.meta


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

502
Samples/UIWidgetSample/Navigation.unity


%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
OcclusionCullingSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_OcclusionBakeSettings:
smallestOccluder: 5
smallestHole: 0.25
backfaceThreshold: 100
m_SceneGUID: 00000000000000000000000000000000
m_OcclusionCullingData: {fileID: 0}
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 9
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
m_FogDensity: 0.01
m_LinearFogStart: 0
m_LinearFogEnd: 300
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
m_AmbientIntensity: 1
m_AmbientMode: 0
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
m_HaloStrength: 0.5
m_FlareStrength: 1
m_FlareFadeSpeed: 3
m_HaloTexture: {fileID: 0}
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
m_DefaultReflectionMode: 0
m_DefaultReflectionResolution: 128
m_ReflectionBounces: 1
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0.44657892, g: 0.4964127, b: 0.5748172, a: 1}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 11
m_GIWorkflowMode: 0
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
m_IndirectOutputScale: 1
m_AlbedoBoost: 1
m_EnvironmentLightingMode: 0
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 1
m_LightmapEditorSettings:
serializedVersion: 10
m_Resolution: 2
m_BakeResolution: 40
m_AtlasSize: 1024
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_Padding: 2
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1
m_TextureCompression: 1
m_FinalGather: 0
m_FinalGatherFiltering: 1
m_FinalGatherRayCount: 256
m_ReflectionCompression: 2
m_MixedBakeMode: 2
m_BakeBackend: 1
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 500
m_PVRBounces: 2
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVRFilteringMode: 1
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_ShowResolutionOverlay: 1
m_LightingDataAsset: {fileID: 0}
m_UseShadowmask: 1
--- !u!196 &4
NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 2
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.4
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
accuratePlacement: 0
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &4286172
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4286176}
- component: {fileID: 4286175}
- component: {fileID: 4286174}
- component: {fileID: 4286173}
m_Layer: 5
m_Name: Canvas
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &4286173
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4286172}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_IgnoreReversedGraphics: 1
m_BlockingObjects: 0
m_BlockingMask:
serializedVersion: 2
m_Bits: 4294967295
--- !u!114 &4286174
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4286172}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UiScaleMode: 0
m_ReferencePixelsPerUnit: 100
m_ScaleFactor: 1
m_ReferenceResolution: {x: 800, y: 600}
m_ScreenMatchMode: 0
m_MatchWidthOrHeight: 0
m_PhysicalUnit: 3
m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 1
--- !u!223 &4286175
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4286172}
m_Enabled: 1
serializedVersion: 3
m_RenderMode: 0
m_Camera: {fileID: 0}
m_PlaneDistance: 100
m_PixelPerfect: 0
m_ReceivesEvents: 1
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_AdditionalShaderChannelsFlag: 0
m_SortingLayerID: 0
m_SortingOrder: 0
m_TargetDisplay: 0
--- !u!224 &4286176
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4286172}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_Children:
- {fileID: 927824195}
m_Father: {fileID: 0}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 0}
--- !u!1 &26611619
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 26611622}
- component: {fileID: 26611621}
- component: {fileID: 26611620}
m_Layer: 0
m_Name: Main Camera
m_TagString: MainCamera
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!81 &26611620
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 26611619}
m_Enabled: 1
--- !u!20 &26611621
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 26611619}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 1
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
m_projectionMatrixMode: 1
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_GateFitMode: 2
m_FocalLength: 50
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
field of view: 60
orthographic: 0
orthographic size: 5
m_Depth: -1
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 1
m_AllowMSAA: 1
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!4 &26611622
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 26611619}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &897547349
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 897547351}
- component: {fileID: 897547350}
m_Layer: 0
m_Name: Directional Light
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!108 &897547350
Light:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 897547349}
m_Enabled: 1
serializedVersion: 8
m_Type: 1
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
m_Intensity: 1
m_Range: 10
m_SpotAngle: 30
m_CookieSize: 10
m_Shadows:
m_Type: 2
m_Resolution: -1
m_CustomResolution: -1
m_Strength: 1
m_Bias: 0.05
m_NormalBias: 0.4
m_NearPlane: 0.2
m_Cookie: {fileID: 0}
m_DrawHalo: 0
m_Flare: {fileID: 0}
m_RenderMode: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_Lightmapping: 4
m_LightShadowCasterMode: 0
m_AreaSize: {x: 1, y: 1}
m_BounceIntensity: 1
m_ColorTemperature: 6570
m_UseColorTemperature: 0
m_ShadowRadius: 0
m_ShadowAngle: 0
--- !u!4 &897547351
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 897547349}
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
m_LocalPosition: {x: 0, y: 3, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
--- !u!1 &927824194
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 927824195}
- component: {fileID: 927824197}
- component: {fileID: 927824196}
m_Layer: 5
m_Name: Navigation
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &927824195
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 927824194}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 4286176}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &927824196
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 927824194}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 020016dfbbaef41b496f4e5be17d098c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Texture: {fileID: 0}
m_UVRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
--- !u!222 &927824197
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 927824194}
m_CullTransparentMesh: 0
--- !u!1 &1838283857
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1838283860}
- component: {fileID: 1838283859}
- component: {fileID: 1838283858}
m_Layer: 0
m_Name: EventSystem
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1838283858
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1838283857}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 1077351063, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_HorizontalAxis: Horizontal
m_VerticalAxis: Vertical
m_SubmitButton: Submit
m_CancelButton: Cancel
m_InputActionsPerSecond: 10
m_RepeatDelay: 0.5
m_ForceModuleActive: 0
--- !u!114 &1838283859
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1838283857}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -619905303, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_FirstSelected: {fileID: 0}
m_sendNavigationEvents: 1
m_DragThreshold: 10
--- !u!4 &1838283860
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1838283857}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

7
Samples/UIWidgetSample/Navigation.unity.meta


fileFormatVersion: 2
guid: 48300882e985d484880e16569661a93c
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

94
Samples/UIWidgetSample/NavigationCanvas.cs


using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using Color = Unity.UIWidgets.ui.Color;
namespace UIWidgetsSample {
public class NavigationCanvas : WidgetCanvas {
protected override string initialRoute => "/";
protected override Dictionary<string, WidgetBuilder> routes => new Dictionary<string, WidgetBuilder> {
{"/", (context) => new HomeScreen()},
{"/detail", (context) => new DetailScreen()}
};
protected override Widget getWidget() {
return null;
}
protected override PageRouteFactory pageRouteBuilder => (RouteSettings settings, WidgetBuilder builder) =>
new PageRouteBuilder(
settings: settings,
pageBuilder: (BuildContext context, Unity.UIWidgets.animation.Animation<double> animation,
Unity.UIWidgets.animation.Animation<double> secondaryAnimation) => builder(context),
transitionsBuilder: (BuildContext context, Animation<double>
animation, Animation<double> secondaryAnimation, Widget child) => new _FadeUpwardsPageTransition(
routeAnimation: animation,
child: child
)
);
}
class HomeScreen : StatelessWidget {
public override Widget build(BuildContext context) {
return new Container(
color: new Color(0xFF888888),
child: new Center(child: new CustomButton(onPressed: () => {
Navigator.pushName(context, "/detail");
}, child: new Text("Go to Detail"))
));
}
}
class DetailScreen : StatelessWidget {
public override Widget build(BuildContext context) {
return new Container(
color: new Color(0xFF1389FD),
child: new Center(child: new CustomButton(onPressed: () => {
Navigator.pop(context);
}, child: new Text("Back"))
));
}
}
class _FadeUpwardsPageTransition : StatelessWidget {
internal _FadeUpwardsPageTransition(
Key key = null,
Animation<double> routeAnimation = null, // The route's linear 0.0 - 1.0 animation.
Widget child = null
) :base(key: key) {
this._positionAnimation = _bottomUpTween.chain(_fastOutSlowInTween).animate(routeAnimation);
this._opacityAnimation = _easeInTween.animate(routeAnimation);
this.child = child;
}
static Tween<Offset> _bottomUpTween = new OffsetTween(
begin: new Offset(0.0, 0.25),
end: Offset.zero
);
static Animatable<double> _fastOutSlowInTween = new CurveTween(curve: Curves.fastOutSlowIn);
static Animatable<double> _easeInTween = new CurveTween(curve: Curves.easeIn);
readonly Animation<Offset> _positionAnimation;
readonly Animation<double> _opacityAnimation;
public readonly Widget child;
public override Widget build(BuildContext context) {
return new SlideTransition(
position: this._positionAnimation,
child: new FadeTransition(
opacity: this._opacityAnimation,
child: this.child
)
);
}
}
}

11
Samples/UIWidgetSample/NavigationCanvas.cs.meta


fileFormatVersion: 2
guid: 020016dfbbaef41b496f4e5be17d098c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存