浏览代码

Implement BottomAppBar and BottomAppBarDemo

/main
Yuncong Zhang 6 年前
当前提交
84bc0348
共有 12 个文件被更改,包括 1058 次插入12 次删除
  1. 146
      Runtime/material/bottom_sheet.cs
  2. 2
      Runtime/material/scaffold.cs
  3. 2
      Samples/UIWidgetsGallery/demo/material/backdrop_demo.cs
  4. 18
      Samples/UIWidgetsGallery/gallery/demos.cs
  5. 113
      Runtime/material/bottom_app_bar.cs
  6. 3
      Runtime/material/bottom_app_bar.cs.meta
  7. 183
      Runtime/material/radio.cs
  8. 3
      Runtime/material/radio.cs.meta
  9. 71
      Runtime/painting/notched_shapes.cs
  10. 3
      Runtime/painting/notched_shapes.cs.meta
  11. 523
      Samples/UIWidgetsGallery/demo/material/bottom_app_bar_demo.cs
  12. 3
      Samples/UIWidgetsGallery/demo/material/bottom_app_bar_demo.cs.meta

146
Runtime/material/bottom_sheet.cs


using System;
using RSG;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;

using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.material {
static class BottomSheetUtils {
public static class BottomSheetUtils {
public static IPromise<object> showModalBottomSheet<T>(
BuildContext context,
WidgetBuilder builder
) {
D.assert(context != null);
D.assert(builder != null);
D.assert(MaterialD.debugCheckHasMaterialLocalizations(context));
return Navigator.push(context, new _ModalBottomSheetRoute<T>(
builder: builder,
theme: Theme.of(context, shadowThemeOnly: true),
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel
));
}
public static PersistentBottomSheetController<object> showBottomSheet(
BuildContext context,
WidgetBuilder builder
) {
D.assert(context != null);
D.assert(builder != null);
return Scaffold.of(context).showBottomSheet(builder);
}
}

onVerticalDragEnd: this._handleDragEnd,
child: bottomSheet
);
}
}
class _ModalBottomSheetLayout : SingleChildLayoutDelegate {
public _ModalBottomSheetLayout(float progress) {
this.progress = progress;
}
public readonly float progress;
public override BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return new BoxConstraints(
minWidth: constraints.maxWidth,
maxWidth: constraints.maxWidth,
minHeight: 0.0f,
maxHeight: constraints.maxHeight * 9.0f / 16.0f
);
}
public override Offset getPositionForChild(Size size, Size childSize) {
return new Offset(0.0f, size.height - childSize.height * this.progress);
}
public override bool shouldRelayout(SingleChildLayoutDelegate _oldDelegate) {
_ModalBottomSheetLayout oldDelegate = _oldDelegate as _ModalBottomSheetLayout;
return this.progress != oldDelegate.progress;
}
}
class _ModalBottomSheet<T> : StatefulWidget {
public _ModalBottomSheet(Key key = null, _ModalBottomSheetRoute<T> route = null) : base(key: key) {
this.route = route;
}
public readonly _ModalBottomSheetRoute<T> route;
public override State createState() {
return new _ModalBottomSheetState<T>();
}
}
class _ModalBottomSheetState<T> : State<_ModalBottomSheet<T>> {
public override Widget build(BuildContext context) {
MediaQueryData mediaQuery = MediaQuery.of(context);
MaterialLocalizations localizations = MaterialLocalizations.of(context);
string routeLabel = "";
return new GestureDetector(
onTap: () => Navigator.pop(context),
child: new AnimatedBuilder(
animation: this.widget.route.animation,
builder: (BuildContext _context, Widget child) => {
float animationValue =
mediaQuery.accessibleNavigation ? 1.0f : this.widget.route.animation.value;
return new ClipRect(
child: new CustomSingleChildLayout(
layoutDelegate: new _ModalBottomSheetLayout(animationValue),
child: new BottomSheet(
animationController: this.widget.route._animationController,
onClosing: () => Navigator.pop(_context),
builder: this.widget.route.builder
)
)
);
}
)
);
}
}
class _ModalBottomSheetRoute<T> : PopupRoute {
public _ModalBottomSheetRoute(
WidgetBuilder builder = null,
ThemeData theme = null,
string barrierLabel = null,
RouteSettings settings = null
) : base(settings: settings) {
this.builder = builder;
this.theme = theme;
this.barrierLabel = barrierLabel;
}
public readonly WidgetBuilder builder;
public readonly ThemeData theme;
public override TimeSpan transitionDuration {
get { return BottomSheetUtils._kBottomSheetDuration; }
}
public override bool barrierDismissible {
get { return true; }
}
public readonly string barrierLabel;
public override Color barrierColor {
get { return Colors.black54; }
}
public AnimationController _animationController;
public override AnimationController createAnimationController() {
D.assert(this._animationController == null);
this._animationController = BottomSheet.createAnimationController(this.navigator.overlay);
return this._animationController;
}
public override Widget buildPage(BuildContext context, Animation<float> animation,
Animation<float> secondaryAnimation) {
Widget bottomSheet = MediaQuery.removePadding(
context: context,
removeTop: true,
child: new _ModalBottomSheet<T>(route: this)
);
if (this.theme != null) {
bottomSheet = new Theme(data: this.theme, child: bottomSheet);
}
return bottomSheet;
}
}
}

2
Runtime/material/scaffold.cs


"The context used was:\n" + context);
}
static ValueListenable<ScaffoldGeometry> geometryOf(BuildContext context) {
public static ValueListenable<ScaffoldGeometry> geometryOf(BuildContext context) {
_ScaffoldScope scaffoldScope =
(_ScaffoldScope) context.inheritFromWidgetOfExactType(typeof(_ScaffoldScope));
if (scaffoldScope == null) {

2
Samples/UIWidgetsGallery/demo/material/backdrop_demo.cs


}
}
public class _BackdropDemoState : SingleTickerProviderStateMixin<BackdropDemo> {
class _BackdropDemoState : SingleTickerProviderStateMixin<BackdropDemo> {
GlobalKey _backdropKey = GlobalKey.key(debugLabel: "Backdrop");
AnimationController _controller;
Category _category = BackdropDemoConstants.allCategories[0];

18
Samples/UIWidgetsGallery/gallery/demos.cs


routeName: BackdropDemo.routeName,
buildRoute: (BuildContext context) => new BackdropDemo()
),
// new GalleryDemo(
// title: "Bottom app bar",
// subtitle: "Optional floating action button notch",
// icon: GalleryIcons.bottom_app_bar,
// category: GalleryDemoCategory._kMaterialComponents,
// routeName: BottomAppBarDemo.routeName,
// documentationUrl: "https://docs.flutter.io/flutter/material/BottomAppBar-class.html",
// buildRoute: (BuildContext context) => BottomAppBarDemo()
// ),
new GalleryDemo(
title: "Bottom app bar",
subtitle: "Optional floating action button notch",
icon: GalleryIcons.bottom_app_bar,
category: DemoUtils._kMaterialComponents,
routeName: BottomAppBarDemo.routeName,
documentationUrl: "https://docs.flutter.io/flutter/material/BottomAppBar-class.html",
buildRoute: (BuildContext context) => new BottomAppBarDemo()
),
// new GalleryDemo(
// title: "Bottom navigation",
// subtitle: "Bottom navigation with cross-fading views",

113
Runtime/material/bottom_app_bar.cs


using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.material {
public class BottomAppBar : StatefulWidget {
public BottomAppBar(
Key key = null,
Color color = null,
float elevation = 8.0f,
NotchedShape shape = null,
Clip clipBehavior = Clip.none,
float notchMargin = 4.0f,
Widget child = null
) : base(key: key) {
D.assert(elevation != null);
D.assert(elevation >= 0.0f);
D.assert(clipBehavior != null);
this.child = child;
this.color = color;
this.elevation = elevation;
this.shape = shape;
this.clipBehavior = clipBehavior;
this.notchMargin = notchMargin;
}
public readonly Widget child;
public readonly Color color;
public readonly float elevation;
public readonly NotchedShape shape;
public readonly Clip clipBehavior;
public readonly float notchMargin;
public override State createState() {
return new _BottomAppBarState();
}
}
class _BottomAppBarState : State<BottomAppBar> {
ValueListenable<ScaffoldGeometry> geometryListenable;
public override void didChangeDependencies() {
base.didChangeDependencies();
this.geometryListenable = Scaffold.geometryOf(this.context);
}
public override Widget build(BuildContext context) {
CustomClipper<Path> clipper = this.widget.shape != null
? (CustomClipper<Path>) new _BottomAppBarClipper(
geometry: this.geometryListenable,
shape: this.widget.shape,
notchMargin: this.widget.notchMargin
)
: new ShapeBorderClipper(shape: new RoundedRectangleBorder());
return new PhysicalShape(
clipper: clipper,
elevation: this.widget.elevation,
color: this.widget.color ?? Theme.of(context).bottomAppBarColor,
clipBehavior: this.widget.clipBehavior,
child: new Material(
type: MaterialType.transparency,
child: this.widget.child == null
? null
: new SafeArea(child: this.widget.child)
)
);
}
}
class _BottomAppBarClipper : CustomClipper<Path> {
public _BottomAppBarClipper(
ValueListenable<ScaffoldGeometry> geometry,
NotchedShape shape,
float notchMargin
) : base(reclip: geometry) {
D.assert(geometry != null);
D.assert(shape != null);
D.assert(notchMargin != null);
this.geometry = geometry;
this.shape = shape;
this.notchMargin = notchMargin;
}
public readonly ValueListenable<ScaffoldGeometry> geometry;
public readonly NotchedShape shape;
public readonly float notchMargin;
public override Path getClip(Size size) {
Rect appBar = Offset.zero & size;
if (this.geometry.value.floatingActionButtonArea == null) {
Path path = new Path();
path.addRect(appBar);
return path;
}
Rect button = this.geometry.value.floatingActionButtonArea
.translate(0.0f, (this.geometry.value.bottomNavigationBarTop * -1.0f) ?? 0.0f);
return this.shape.getOuterPath(appBar, button.inflate(this.notchMargin));
}
public override bool shouldReclip(CustomClipper<Path> oldClipper) {
return (oldClipper as _BottomAppBarClipper).geometry != this.geometry;
}
}
}

3
Runtime/material/bottom_app_bar.cs.meta


fileFormatVersion: 2
guid: aa89369405044a62a19225b2c20815f6
timeCreated: 1553156127

183
Runtime/material/radio.cs


using System;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.material {
class RadioUtils {
public const float _kOuterRadius = 8.0f;
public const float _kInnerRadius = 4.5f;
}
public class Radio<T> : StatefulWidget where T : class {
public Radio(
Key key = null,
T value = null,
T groupValue = null,
ValueChanged<T> onChanged = null,
Color activeColor = null,
MaterialTapTargetSize? materialTapTargetSize = null
) : base(key: key) {
D.assert(value != null);
D.assert(groupValue != null);
D.assert(onChanged != null);
this.value = value;
this.groupValue = groupValue;
this.onChanged = onChanged;
this.activeColor = activeColor;
this.materialTapTargetSize = materialTapTargetSize;
}
public readonly T value;
public readonly T groupValue;
public readonly ValueChanged<T> onChanged;
public readonly Color activeColor;
public readonly MaterialTapTargetSize? materialTapTargetSize;
public override State createState() {
return new _RadioState<T>();
}
}
class _RadioState<T> : TickerProviderStateMixin<Radio<T>> where T : class {
bool _enabled {
get { return this.widget.onChanged != null; }
}
Color _getInactiveColor(ThemeData themeData) {
return this._enabled ? themeData.unselectedWidgetColor : themeData.disabledColor;
}
void _handleChanged(bool? selected) {
if (selected == true) {
this.widget.onChanged(this.widget.value);
}
}
public override Widget build(BuildContext context) {
D.assert(MaterialD.debugCheckHasMaterial(context));
ThemeData themeData = Theme.of(context);
Size size;
switch (this.widget.materialTapTargetSize ?? themeData.materialTapTargetSize) {
case MaterialTapTargetSize.padded:
size = new Size(2 * Constants.kRadialReactionRadius + 8.0f,
2 * Constants.kRadialReactionRadius + 8.0f);
break;
case MaterialTapTargetSize.shrinkWrap:
size = new Size(2 * Constants.kRadialReactionRadius, 2 * Constants.kRadialReactionRadius);
break;
default:
throw new Exception("Unknown material tap target size");
}
BoxConstraints additionalConstraints = BoxConstraints.tight(size);
return new _RadioRenderObjectWidget(
selected: this.widget.value == this.widget.groupValue,
activeColor: this.widget.activeColor ?? themeData.toggleableActiveColor,
inactiveColor: this._getInactiveColor(themeData),
onChanged: this._enabled ? this._handleChanged : (ValueChanged<bool?>) null,
additionalConstraints: additionalConstraints,
vsync: this
);
}
}
class _RadioRenderObjectWidget : LeafRenderObjectWidget {
public _RadioRenderObjectWidget(
Key key = null,
bool? selected = null,
Color activeColor = null,
Color inactiveColor = null,
BoxConstraints additionalConstraints = null,
ValueChanged<bool?> onChanged = null,
TickerProvider vsync = null
) : base(key: key) {
D.assert(selected != null);
D.assert(activeColor != null);
D.assert(inactiveColor != null);
D.assert(additionalConstraints != null);
D.assert(vsync != null);
this.selected = selected;
this.activeColor = activeColor;
this.inactiveColor = inactiveColor;
this.additionalConstraints = additionalConstraints;
this.onChanged = onChanged;
this.vsync = vsync;
}
public readonly bool? selected;
public readonly Color activeColor;
public readonly Color inactiveColor;
public readonly BoxConstraints additionalConstraints;
public readonly ValueChanged<bool?> onChanged;
public readonly TickerProvider vsync;
public override RenderObject createRenderObject(BuildContext context) {
return new _RenderRadio(
value: this.selected,
activeColor: this.activeColor,
inactiveColor: this.inactiveColor,
onChanged: this.onChanged,
vsync: this.vsync,
additionalConstraints: this.additionalConstraints
);
}
public override void updateRenderObject(BuildContext context, RenderObject _renderObject) {
_RenderRadio renderObject = _renderObject as _RenderRadio;
renderObject.value = this.selected;
renderObject.activeColor = this.activeColor;
renderObject.inactiveColor = this.inactiveColor;
renderObject.onChanged = this.onChanged;
renderObject.additionalConstraints = this.additionalConstraints;
renderObject.vsync = this.vsync;
}
}
class _RenderRadio : RenderToggleable {
public _RenderRadio(
bool? value,
Color activeColor,
Color inactiveColor,
ValueChanged<bool?> onChanged,
BoxConstraints additionalConstraints,
TickerProvider vsync
) : base(
value: value,
tristate: false,
activeColor: activeColor,
inactiveColor: inactiveColor,
onChanged: onChanged,
additionalConstraints: additionalConstraints,
vsync: vsync
) {
}
public override void paint(PaintingContext context, Offset offset) {
Canvas canvas = context.canvas;
this.paintRadialReaction(canvas, offset,
new Offset(Constants.kRadialReactionRadius, Constants.kRadialReactionRadius));
Offset center = (offset & this.size).center;
Color radioColor = this.onChanged != null ? this.activeColor : this.inactiveColor;
Paint paint = new Paint();
paint.color = Color.lerp(this.inactiveColor, radioColor, this.position.value);
paint.style = PaintingStyle.stroke;
paint.strokeWidth = 2.0f;
canvas.drawCircle(center, RadioUtils._kOuterRadius, paint);
if (!this.position.isDismissed) {
paint.style = PaintingStyle.fill;
canvas.drawCircle(center, RadioUtils._kInnerRadius * this.position.value, paint);
}
}
}
}

3
Runtime/material/radio.cs.meta


fileFormatVersion: 2
guid: 1328fb437a4047c49b51dfe3a2c3d106
timeCreated: 1553154501

71
Runtime/painting/notched_shapes.cs


using System.Collections.Generic;
using Unity.UIWidgets.ui;
using UnityEngine;
using Rect = Unity.UIWidgets.ui.Rect;
namespace Unity.UIWidgets.painting {
public abstract class NotchedShape {
public NotchedShape() {
}
public abstract Path getOuterPath(Rect host, Rect guest);
}
public class CircularNotchedRectangle : NotchedShape {
public CircularNotchedRectangle() {
}
public override Path getOuterPath(Rect host, Rect guest) {
if (!host.overlaps(guest)) {
Path path = new Path();
path.addRect(host);
return path;
}
float notchRadius = guest.width / 2.0f;
const float s1 = 15.0f;
const float s2 = 1.0f;
float r = notchRadius;
float a = -1.0f * r - s2;
float b = host.top - guest.center.dy;
float n2 = Mathf.Sqrt(b * b * r * r * (a * a + b * b - r * r));
float p2xA = ((a * r * r) - n2) / (a * a + b * b);
float p2xB = ((a * r * r) + n2) / (a * a + b * b);
float p2yA = Mathf.Sqrt(r * r - p2xA * p2xA);
float p2yB = Mathf.Sqrt(r * r - p2xB * p2xB);
List<Offset> p = new List<Offset>(6);
p[0] = new Offset(a - s1, b);
p[1] = new Offset(a, b);
float cmp = b < 0 ? -1.0f : 1.0f;
p[2] = cmp * p2yA > cmp * p2yB ? new Offset(p2xA, p2yA) : new Offset(p2xB, p2yB);
p[3] = new Offset(-1.0f * p[2].dx, p[2].dy);
p[4] = new Offset(-1.0f * p[1].dx, p[1].dy);
p[5] = new Offset(-1.0f * p[0].dx, p[0].dy);
for (int i = 0; i < p.Count; i += 1) {
p[i] += guest.center;
}
Path ret = new Path();
ret.moveTo(host.left, host.top);
ret.lineTo(p[0].dx, p[0].dy);
ret.quadTo(p[1].dx, p[1].dy, p[2].dx, p[2].dy);
// TODO: replace this lineTo() with arcToPoint when arcToPoint is ready
ret.lineTo(p[3].dx, p[3].dy);
// ret.arcToPoint(p[3], p[3], radius: Radius.circular(notchRadius), clockwise: false);
ret.quadTo(p[4].dx, p[4].dy, p[5].dx, p[5].dy);
ret.lineTo(host.right, host.top);
ret.lineTo(host.right, host.bottom);
ret.lineTo(host.left, host.bottom);
ret.close();
return ret;
}
}
}

3
Runtime/painting/notched_shapes.cs.meta


fileFormatVersion: 2
guid: c89c524fa7c242aa91900b63841be8b3
timeCreated: 1553153571

523
Samples/UIWidgetsGallery/demo/material/bottom_app_bar_demo.cs


using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Canvas = Unity.UIWidgets.ui.Canvas;
using Color = Unity.UIWidgets.ui.Color;
using Material = Unity.UIWidgets.material.Material;
using Rect = Unity.UIWidgets.ui.Rect;
namespace UIWidgetsGallery.gallery {
public class BottomAppBarDemo : StatefulWidget {
public const string routeName = "/material/bottom_app_bar";
public override State createState() {
return new _BottomAppBarDemoState();
}
}
class _BottomAppBarDemoState : State<BottomAppBarDemo> {
static readonly GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>.key();
static readonly _ChoiceValue<Widget> kNoFab = new _ChoiceValue<Widget>(
title: "None",
label: "do not show a floating action button",
value: null
);
static readonly _ChoiceValue<Widget> kCircularFab = new _ChoiceValue<Widget>(
title: "Circular",
label: "circular floating action button",
value: new FloatingActionButton(
onPressed: _showSnackbar,
child: new Icon(Icons.add),
backgroundColor: Colors.orange
)
);
static readonly _ChoiceValue<Widget> kDiamondFab = new _ChoiceValue<Widget>(
title: "Diamond",
label: "diamond shape floating action button",
value: new _DiamondFab(
onPressed: _showSnackbar,
child: new Icon(Icons.add)
)
);
static readonly _ChoiceValue<bool> kShowNotchTrue = new _ChoiceValue<bool>(
title: "On",
label: "show bottom appbar notch",
value: true
);
static readonly _ChoiceValue<bool> kShowNotchFalse = new _ChoiceValue<bool>(
title: "Off",
label: "do not show bottom appbar notch",
value: false
);
static readonly _ChoiceValue<FloatingActionButtonLocation> kFabEndDocked =
new _ChoiceValue<FloatingActionButtonLocation>(
title: "Attached - End",
label: "floating action button is docked at the end of the bottom app bar",
value: FloatingActionButtonLocation.endDocked
);
static readonly _ChoiceValue<FloatingActionButtonLocation> kFabCenterDocked =
new _ChoiceValue<FloatingActionButtonLocation>(
title: "Attached - Center",
label: "floating action button is docked at the center of the bottom app bar",
value: FloatingActionButtonLocation.centerDocked
);
static readonly _ChoiceValue<FloatingActionButtonLocation> kFabEndFloat =
new _ChoiceValue<FloatingActionButtonLocation>(
title: "Free - End",
label: "floating action button floats above the end of the bottom app bar",
value: FloatingActionButtonLocation.endFloat
);
static readonly _ChoiceValue<FloatingActionButtonLocation> kFabCenterFloat =
new _ChoiceValue<FloatingActionButtonLocation>(
title: "Free - Center",
label: "floating action button is floats above the center of the bottom app bar",
value: FloatingActionButtonLocation.centerFloat
);
static void _showSnackbar() {
const string text =
"When the Scaffold's floating action button location changes, " +
"the floating action button animates to its new position." +
"The BottomAppBar adapts its shape appropriately.";
_scaffoldKey.currentState.showSnackBar(
new SnackBar(content: new Text(text))
);
}
static readonly List<_NamedColor> kBabColors = new List<_NamedColor> {
new _NamedColor(null, "Clear"),
new _NamedColor(new Color(0xFFFFC100), "Orange"),
new _NamedColor(new Color(0xFF91FAFF), "Light Blue"),
new _NamedColor(new Color(0xFF00D1FF), "Cyan"),
new _NamedColor(new Color(0xFF00BCFF), "Cerulean"),
new _NamedColor(new Color(0xFF009BEE), "Blue")
};
_ChoiceValue<Widget> _fabShape = kCircularFab;
_ChoiceValue<bool> _showNotch = kShowNotchTrue;
_ChoiceValue<FloatingActionButtonLocation> _fabLocation = kFabEndDocked;
Color _babColor = kBabColors.First().color;
void _onShowNotchChanged(_ChoiceValue<bool> value) {
this.setState(() => { this._showNotch = value; });
}
void _onFabShapeChanged(_ChoiceValue<Widget> value) {
this.setState(() => { this._fabShape = value; });
}
void _onFabLocationChanged(_ChoiceValue<FloatingActionButtonLocation> value) {
this.setState(() => { this._fabLocation = value; });
}
void _onBabColorChanged(Color value) {
this.setState(() => { this._babColor = value; });
}
public override Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
appBar: new AppBar(
title: new Text("Bottom app bar"),
elevation: 0.0f,
actions: new List<Widget> {
new MaterialDemoDocumentationButton(BottomAppBarDemo.routeName),
new IconButton(
icon: new Icon(Icons.sentiment_very_satisfied),
onPressed: () => {
this.setState(() => {
this._fabShape = this._fabShape == kCircularFab ? kDiamondFab : kCircularFab;
});
}
)
}
),
body: new ListView(
padding: EdgeInsets.only(bottom: 88.0f),
children: new List<Widget> {
new _AppBarDemoHeading("FAB Shape"),
new _RadioItem<Widget>(kCircularFab, this._fabShape, this._onFabShapeChanged),
new _RadioItem<Widget>(kDiamondFab, this._fabShape, this._onFabShapeChanged),
new _RadioItem<Widget>(kNoFab, this._fabShape, this._onFabShapeChanged),
new Divider(),
new _AppBarDemoHeading("Notch"),
new _RadioItem<bool>(kShowNotchTrue, this._showNotch, this._onShowNotchChanged),
new _RadioItem<bool>(kShowNotchFalse, this._showNotch, this._onShowNotchChanged),
new Divider(),
new _AppBarDemoHeading("FAB Position"),
new _RadioItem<FloatingActionButtonLocation>(kFabEndDocked, this._fabLocation,
this._onFabLocationChanged),
new _RadioItem<FloatingActionButtonLocation>(kFabCenterDocked, this._fabLocation,
this._onFabLocationChanged),
new _RadioItem<FloatingActionButtonLocation>(kFabEndFloat, this._fabLocation,
this._onFabLocationChanged),
new _RadioItem<FloatingActionButtonLocation>(kFabCenterFloat, this._fabLocation,
this._onFabLocationChanged),
new Divider(),
new _AppBarDemoHeading("App bar color"),
new _ColorsItem(kBabColors, this._babColor, this._onBabColorChanged)
}
),
floatingActionButton:
this._fabShape.value,
floatingActionButtonLocation:
this._fabLocation.value,
bottomNavigationBar: new _DemoBottomAppBar(
color: this._babColor,
fabLocation: this._fabLocation.value,
shape: this._selectNotch()
)
);
}
NotchedShape _selectNotch() {
if (!this._showNotch.value) {
return null;
}
if (this._fabShape == kCircularFab) {
return new CircularNotchedRectangle();
}
if (this._fabShape == kDiamondFab) {
return new _DiamondNotchedRectangle();
}
return null;
}
}
class _ChoiceValue<T> {
public _ChoiceValue(T value, string title, string label) {
this.value = value;
this.title = title;
this.label = label;
}
public readonly T value;
public readonly string title;
string label; // For the Semantics widget that contains title
public override string ToString() {
return $"{this.GetType()}('{this.title}')";
}
}
class _RadioItem<T> : StatelessWidget {
public _RadioItem(_ChoiceValue<T> value, _ChoiceValue<T> groupValue, ValueChanged<_ChoiceValue<T>> onChanged) {
this.value = value;
this.groupValue = groupValue;
this.onChanged = onChanged;
}
_ChoiceValue<T> value;
_ChoiceValue<T> groupValue;
ValueChanged<_ChoiceValue<T>> onChanged;
public override Widget build(BuildContext context) {
ThemeData theme = Theme.of(context);
return new Container(
height: 56.0f,
padding: EdgeInsets.only(left: 16.0f),
alignment: Alignment.centerLeft,
child: new Row(
children: new List<Widget> {
new Radio<_ChoiceValue<T>>(
value: this.value,
groupValue: this.groupValue,
onChanged: this.onChanged
),
new Expanded(
child: new GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => { this.onChanged(this.value); },
child: new Text(this.value.title,
style: theme.textTheme.subhead
)
)
)
}
)
);
}
}
class _NamedColor {
public _NamedColor(Color color, string name) {
this.color = color;
this.name = name;
}
public readonly Color color;
public readonly string name;
}
class _ColorsItem : StatelessWidget {
public _ColorsItem(List<_NamedColor> colors, Color selectedColor, ValueChanged<Color> onChanged) {
this.colors = colors;
this.selectedColor = selectedColor;
this.onChanged = onChanged;
}
List<_NamedColor> colors;
public readonly Color selectedColor;
ValueChanged<Color> onChanged;
public override Widget build(BuildContext context) {
return new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: this.colors.Select<_NamedColor, Widget>((_NamedColor namedColor) => {
return new RawMaterialButton(
onPressed: () => { this.onChanged(namedColor.color); },
constraints: BoxConstraints.tightFor(
width: 32.0f,
height: 32.0f
),
fillColor: namedColor.color,
shape: new CircleBorder(
side: new BorderSide(
color: namedColor.color == this.selectedColor ? Colors.black : new Color(0xFFD5D7DA),
width: 2.0f
)
)
);
}).ToList()
);
}
}
class _AppBarDemoHeading : StatelessWidget {
public _AppBarDemoHeading(string text) {
this.text = text;
}
public readonly string text;
public override Widget build(BuildContext context) {
ThemeData theme = Theme.of(context);
return new Container(
height: 48.0f,
padding: EdgeInsets.only(left: 56.0f),
alignment: Alignment.centerLeft,
child: new Text(this.text,
style: theme.textTheme.body1.copyWith(
color: theme.primaryColor
)
)
);
}
}
class _DemoBottomAppBar : StatelessWidget {
public _DemoBottomAppBar(
Color color = null,
FloatingActionButtonLocation fabLocation = null,
NotchedShape shape = null
) {
this.color = color;
this.fabLocation = fabLocation;
this.shape = shape;
}
public readonly Color color;
public readonly FloatingActionButtonLocation fabLocation;
public readonly NotchedShape shape;
static readonly List<FloatingActionButtonLocation> kCenterLocations = new List<FloatingActionButtonLocation> {
FloatingActionButtonLocation.centerDocked,
FloatingActionButtonLocation.centerFloat
};
public override Widget build(BuildContext context) {
List<Widget> rowContents = new List<Widget> {
new IconButton(
icon: new Icon(Icons.menu),
onPressed: () => {
BottomSheetUtils.showModalBottomSheet<object>(
context: context,
builder: (BuildContext _context) => new _DemoDrawer()
);
}
)
};
if (kCenterLocations.Contains(this.fabLocation)) {
rowContents.Add(
new Expanded(child: new SizedBox())
);
}
rowContents.AddRange(new List<Widget> {
new IconButton(
icon: new Icon(Icons.search),
onPressed: () => {
Scaffold.of(context).showSnackBar(
new SnackBar(content: new Text("This is a dummy search action."))
);
}
),
new IconButton(
icon: new Icon(
Theme.of(context).platform == RuntimePlatform.Android
? Icons.more_vert
: Icons.more_horiz
),
onPressed: () => {
Scaffold.of(context).showSnackBar(
new SnackBar(content: new Text("This is a dummy menu action."))
);
}
)
});
return new BottomAppBar(
color: this.color,
child: new Row(children: rowContents),
shape: this.shape
);
}
}
class _DemoDrawer : StatelessWidget {
public _DemoDrawer() {
}
public override Widget build(BuildContext context) {
return new Drawer(
child: new Column(
children: new List<Widget> {
new ListTile(
leading: new Icon(Icons.search),
title: new Text("Search")
),
new ListTile(
leading: new Icon(Icons.threed_rotation),
title: new Text("3D")
)
}
)
);
}
}
class _DiamondFab : StatelessWidget {
public _DiamondFab(
Widget child,
VoidCallback onPressed
) {
this.child = child;
this.onPressed = onPressed;
}
public readonly Widget child;
public readonly VoidCallback onPressed;
public override Widget build(BuildContext context) {
return new Material(
shape: new _DiamondBorder(),
color: Colors.orange,
child: new InkWell(
onTap: this.onPressed == null ? (GestureTapCallback) null : () => { this.onPressed(); },
child: new Container(
width: 56.0f,
height: 56.0f,
child: IconTheme.merge(
data: new IconThemeData(color: Theme.of(context).accentIconTheme.color),
child: this.child
)
)
),
elevation: 6.0f
);
}
}
class _DiamondNotchedRectangle : NotchedShape {
public _DiamondNotchedRectangle() {
}
public override Path getOuterPath(Rect host, Rect guest) {
if (!host.overlaps(guest)) {
Path path = new Path();
path.addRect(host);
return path;
}
D.assert(guest.width > 0.0f);
Rect intersection = guest.intersect(host);
float notchToCenter =
intersection.height * (guest.height / 2.0f)
/ (guest.width / 2.0f);
Path ret = new Path();
ret.moveTo(host.left, host.top);
ret.lineTo(guest.center.dx - notchToCenter, host.top);
ret.lineTo(guest.left + guest.width / 2.0f, guest.bottom);
ret.lineTo(guest.center.dx + notchToCenter, host.top);
ret.lineTo(host.right, host.top);
ret.lineTo(host.right, host.bottom);
ret.lineTo(host.left, host.bottom);
ret.close();
return ret;
}
}
class _DiamondBorder : ShapeBorder {
public _DiamondBorder() {
}
public override EdgeInsets dimensions {
get { return EdgeInsets.only(); }
}
public override Path getInnerPath(Rect rect) {
return this.getOuterPath(rect);
}
public override Path getOuterPath(Rect rect) {
Path path = new Path();
path.moveTo(rect.left + rect.width / 2.0f, rect.top);
path.lineTo(rect.right, rect.top + rect.height / 2.0f);
path.lineTo(rect.left + rect.width / 2.0f, rect.bottom);
path.lineTo(rect.left, rect.top + rect.height / 2.0f);
path.close();
return path;
}
public override void paint(Canvas canvas, Rect rect) {
}
public override ShapeBorder scale(float t) {
return null;
}
}
}

3
Samples/UIWidgetsGallery/demo/material/bottom_app_bar_demo.cs.meta


fileFormatVersion: 2
guid: 7c1d20efeafe42699212e2147c0828af
timeCreated: 1553149093
正在加载...
取消
保存