浏览代码

add shrine demos

/zgh-devtools
guanghuispark 4 年前
当前提交
b0cfb93e
共有 22 个文件被更改,包括 2169 次插入12 次删除
  1. 4
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_navigation_demo.cs
  2. 2
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/model/app_state_model.cs
  3. 4
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/model/product.cs
  4. 2
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/gallery/backdrop.cs
  5. 4
      com.unity.uiwidgets/Runtime/animation/tween_sequence.cs
  6. 2
      com.unity.uiwidgets/Runtime/painting/image_provider.cs
  7. 4
      com.unity.uiwidgets/Runtime/widgets/animated_list.cs
  8. 2
      com.unity.uiwidgets/Runtime/widgets/heroes.cs
  9. 119
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/app.cs
  10. 399
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/backdrop.cs
  11. 85
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/category_menu_page.cs
  12. 22
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/colors.cs
  13. 655
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/expanding_bottom_sheet.cs
  14. 53
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/home.cs
  15. 136
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/login.cs
  16. 283
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/shopping_cart.cs
  17. 81
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/supplemental/asymmetric_view.cs
  18. 137
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/supplemental/cut_corners_border.cs
  19. 102
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/supplemental/product_card.cs
  20. 85
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/supplemental/product_columns.cs
  21. 0
      /Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/logo.png

4
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_navigation_demo.cs


decoration: new BoxDecoration(
image: new DecorationImage(
image: new FileImage(
file: "test.png"
file: "logo.png"
)
),
shape: BoxShape.circle

decoration: new BoxDecoration(
image: new DecorationImage(
image: new FileImage(
"test.png"
"logo.png"
)
),
shape: BoxShape.circle

2
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/model/app_state_model.cs


private Dictionary<int, int> _productsInCart = new Dictionary<int, int> { };
private Dictionary<int, int> productsInCart => new Dictionary<int, int>(this._productsInCart);
public Dictionary<int, int> productsInCart => new Dictionary<int, int>(this._productsInCart);
public int totalCartQuantity => this._productsInCart.Values.Sum();

4
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/model/product.cs


public readonly string name;
public readonly int price;
private string assetName => $"{this.id}-0.jpg";
private string assetPackage => "shrine_images";
public string assetName => $"{this.id}-0.jpg";
public string assetPackage => "shrine_images";
public override string ToString()

2
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/gallery/backdrop.cs


}
}
internal class Backdrop : StatefulWidget
public class Backdrop : StatefulWidget
{
public Backdrop(
Widget frontAction = null,

4
com.unity.uiwidgets/Runtime/animation/tween_sequence.cs


using Transform = Unity.UIWidgets.widgets.Transform;
namespace Unity.UIWidgets.animation {
class TweenSequence<T> : Animatable<T> {
public class TweenSequence<T> : Animatable<T> {
public TweenSequence(List<TweenSequenceItem<T>> items) {
D.assert(items != null);
D.assert(items.isNotEmpty);

public override float transform(float t) => 1 - base.transform(1 - t);
}
class TweenSequenceItem<T> {
public class TweenSequenceItem<T> {
public TweenSequenceItem(
Animatable<T> tween = null,

2
com.unity.uiwidgets/Runtime/painting/image_provider.cs


}
Future<Codec> _loadAsync(FileImage key, DecoderCallback decode) {
byte[] bytes = File.ReadAllBytes(Path.Combine(Application.dataPath, key.file));
byte[] bytes = File.ReadAllBytes(Path.Combine(Application.streamingAssetsPath, key.file));
if (bytes.Length > 0) {
return decode(bytes);
}

4
com.unity.uiwidgets/Runtime/widgets/animated_list.cs


public readonly GlobalKey<SliverAnimatedListState> _sliverAnimatedListKey =
GlobalKey<SliverAnimatedListState>.key();
void insertItem(int index, TimeSpan? duration = null ) {
public void insertItem(int index, TimeSpan? duration = null ) {
void removeItem(int index, AnimatedListRemovedItemBuilder builder, TimeSpan? duration = null ) {
public void removeItem(int index, AnimatedListRemovedItemBuilder builder, TimeSpan? duration = null ) {
duration = duration ?? _kDuration;
_sliverAnimatedListKey.currentState.removeItem(index, builder, duration: duration);
}

2
com.unity.uiwidgets/Runtime/widgets/heroes.cs


}
}
foreach (object tag in toHeroes.Keys) {
_HeroState heroState = new _HeroState();
_HeroState heroState = null;
if (!fromHeroes.TryGetValue(tag, out heroState)) {
toHeroes[tag].ensurePlaceholderIsHidden();
}

119
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/app.cs


using UIWidgetsGallery.demo.shrine;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery.demo.shrine
{
public class ShrineApp : StatefulWidget {
public override State createState() => new _ShrineAppState();
}
public class _ShrineAppState : State<ShrineApp> {//with SingleTickerProviderStateMixin {
AnimationController _controller;
public override void initState() {
base.initState();
_controller = new AnimationController(
vsync: this,
duration: Durantion(milliseconds: 450),
value: 1.0
);
}
public override Widget build(BuildContext context) {
return new MaterialApp(
title: "Shrine",
home: HomePage(
backdrop: Backdrop(
frontLayer: ProductPage(),
backLayer: CategoryMenuPage(onCategoryTap: () => _controller.forward()),
frontTitle: new Text("SHRINE"),
backTitle: new Text("MENU"),
controller: _controller
),
expandingBottomSheet: ExpandingBottomSheet(hideController: _controller),
),
initialRoute: "/login",
onGenerateRoute: _getRoute,
theme: _kShrineTheme.copyWith(platform: Theme.of(context).platform),
);
}
}
Route<object> _getRoute(RouteSettings settings) {
if (settings.name != '/login') {
return null;
}
return new MaterialPageRoute(
settings: settings,
builder: (BuildContext context) => LoginPage(),
fullscreenDialog: true
);
}
public readonly ThemeData _kShrineTheme = _buildShrineTheme();
IconThemeData _customIconTheme(IconThemeData original) {
return original.copyWith(color: shrineColorsUtils.kShrineBrown900);
}
ThemeData _buildShrineTheme() {
ThemeData _base = ThemeData.light();
return _base.copyWith(
colorScheme: kShrineColorScheme,
accentColor: shrineColorsUtils.kShrineBrown900,
primaryColor: shrineColorsUtils.kShrinePink100,
buttonColor: shrineColorsUtils.kShrinePink100,
scaffoldBackgroundColor: shrineColorsUtils.kShrineBackgroundWhite,
cardColor: shrineColorsUtils.kShrineBackgroundWhite,
textSelectionColor: shrineColorsUtils.kShrinePink100,
errorColor: shrineColorsUtils.kShrineErrorRed,
buttonTheme: new ButtonThemeData(
colorScheme: kShrineColorScheme,
textTheme: ButtonTextTheme.normal
),
primaryIconTheme: _customIconTheme(_base.iconTheme),
inputDecorationTheme: new InputDecorationTheme(border: CutCornersBorder()),
textTheme: _buildShrineTextTheme(_base.textTheme),
primaryTextTheme: _buildShrineTextTheme(_base.primaryTextTheme),
accentTextTheme: _buildShrineTextTheme(_base.accentTextTheme),
iconTheme: _customIconTheme(_base.iconTheme)
);
}
TextTheme _buildShrineTextTheme(TextTheme _base) {
return _base.copyWith(
headline5: _base.headline5.copyWith(fontWeight: FontWeight.w500),
headline6: _base.headline6.copyWith(fontSize: 18.0f),
caption: _base.caption.copyWith(fontWeight: FontWeight.w400, fontSize: 14.0f),
bodyText1: _base.bodyText1.copyWith(fontWeight: FontWeight.w500, fontSize: 16.0f),
button: _base.button.copyWith(fontWeight: FontWeight.w500, fontSize: 14.0f)
).apply(
fontFamily: "Raleway",
displayColor: shrineColorsUtils.kShrineBrown900,
bodyColor: shrineColorsUtils.kShrineBrown900
);
}
ColorScheme kShrineColorScheme = new ColorScheme(
primary: shrineColorsUtils.kShrinePink100,
primaryVariant: shrineColorsUtils.kShrineBrown900,
secondary: shrineColorsUtils.kShrinePink50,
secondaryVariant: shrineColorsUtils.kShrineBrown900,
surface: shrineColorsUtils.kShrineSurfaceWhite,
background: shrineColorsUtils.kShrineBackgroundWhite,
error: shrineColorsUtils.kShrineErrorRed,
onPrimary: shrineColorsUtils.kShrineBrown900,
onSecondary: shrineColorsUtils.kShrineBrown900,
onSurface: shrineColorsUtils.kShrineBrown900,
onBackground: shrineColorsUtils.kShrineBrown900,
onError: shrineColorsUtils.kShrineSurfaceWhite,
brightness: Brightness.light
);
}

399
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/backdrop.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery.demo.shrine
{
public class backdropUtils
{
public static readonly Cubic _kAccelerateCurve = new Cubic(0.548f, 0.0f, 0.757f, 0.464f);
public static readonly Cubic _kDecelerateCurve = new Cubic(0.23f, 0.94f, 0.41f, 1.0f);
public static readonly float _kPeakVelocityTime = 0.248210f;
public static readonly float _kPeakVelocityProgress = 0.379146f;
}
public class _TappableWhileStatusIs : StatefulWidget {
public _TappableWhileStatusIs(
AnimationStatus status,
Key key = null,
AnimationController controller = null,
Widget child = null
) : base(key: key)
{
this.status = status;
this.controller = controller;
this.child = child;
}
public readonly AnimationController controller;
public readonly AnimationStatus status;
public readonly Widget child;
public override State createState() => new _TappableWhileStatusIsState();
}
public class _TappableWhileStatusIsState : State<_TappableWhileStatusIs> {
bool _active;
public override void initState() {
base.initState();
widget.controller.addStatusListener(_handleStatusChange);
_active = widget.controller.status == widget.status;
}
public override void dispose() {
widget.controller.removeStatusListener(_handleStatusChange);
base.dispose();
}
void _handleStatusChange(AnimationStatus status) {
bool value = widget.controller.status == widget.status;
if (_active != value) {
setState(() => {
_active = value;
});
}
}
public override Widget build(BuildContext context) {
Widget child = new AbsorbPointer(
absorbing: !_active,
child: widget.child
);
if (!_active) {
child = new FocusScope(
canRequestFocus: false,
debugLabel: "_TappableWhileStatusIs",
child: child
);
}
return child;
}
}
public class _FrontLayer : StatelessWidget {
public _FrontLayer(
Key key = null,
VoidCallback onTap = null,
Widget child = null
) : base(key: key)
{
this.onTap = onTap;
this.child = child;
}
public readonly VoidCallback onTap;
public readonly Widget child;
public override Widget build(BuildContext context) {
return new Material(
elevation: 16.0f,
shape: new BeveledRectangleBorder(
borderRadius: BorderRadius.only(topLeft: Radius.circular(46.0f))
),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: new List<Widget>{
new GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: new Container(
height: 40.0f,
alignment: AlignmentDirectional.centerStart
)
),
new Expanded(
child: child
),
}
)
);
}
}
public class _BackdropTitle : AnimatedWidget {
public _BackdropTitle(
Key key,
Animation<float> listenable,
Delegate onPress,
Widget frontTitle,
Widget backTitle
) : base(key: key, listenable: listenable)
{
D.assert(frontTitle != null);
D.assert(backTitle != null);
this.backTitle = backTitle;
this.frontTitle = frontTitle;
this.onPress = onPress;
}
public readonly Delegate onPress;
//public delegate void onPress(); [!!!]
public readonly Widget frontTitle;
public readonly Widget backTitle;
protected override Widget build(BuildContext context) {
Animation<float> animation = new CurvedAnimation(
parent: listenable as Animation<float>,
curve: new Interval(0.0f, 0.78f)
);
return new DefaultTextStyle(
style: Theme.of(context).primaryTextTheme.headline6,
softWrap: false,
overflow: TextOverflow.ellipsis,
child: new Row(children: new List<Widget>{
new SizedBox(
width: 72.0f,
child: new IconButton(
padding: EdgeInsets.only(right: 8.0f),
onPressed: onPress,
icon: new Stack(children: new List<Widget>{
new Opacity(
opacity: animation.value,
child: new ImageIcon(new FileImage("packages/shrine_images/slanted_menu.png"))
),
new FractionalTranslation(
translation: new Tween<Offset>(
begin: Offset.zero,
end: new Offset(1.0f, 0.0f)).evaluate(animation),
child: new ImageIcon(new FileImage("packages/shrine_images/diamond.png"))
)
})
)
),
new Stack(
children: new List<Widget>{
new Opacity(
opacity: new CurvedAnimation(
parent: new ReverseAnimation(animation),
curve: new Interval(0.5f, 1.0f)
).value,
child: new FractionalTranslation(
translation: new Tween<Offset>(
begin: Offset.zero,
end: new Offset(0.5f, 0.0f)).evaluate(animation),
child: backTitle
)
),
new Opacity(
opacity: new CurvedAnimation(
parent: animation,
curve: new Interval(0.5f, 1.0f)).value,
child: new FractionalTranslation(
translation: new Tween<Offset>(
begin: new Offset(-0.25f, 0.0f),
end: Offset.zero).evaluate(animation),
child: frontTitle
)
),
}
),
})
);
}
}
public class Backdrop : StatefulWidget {
public Backdrop(
Widget frontLayer,
Widget backLayer,
Widget frontTitle,
Widget backTitle,
AnimationController controller
) {
D.assert(frontLayer != null);
D.assert(backLayer != null);
D.assert(frontTitle != null);
D.assert(backTitle != null);
D.assert(controller != null);
this.frontLayer = frontLayer;
this.backLayer = backLayer;
this.frontTitle = frontTitle;
this.backTitle = backTitle;
this.controller = controller;
}
public readonly Widget frontLayer;
public readonly Widget backLayer;
public readonly Widget frontTitle;
public readonly Widget backTitle;
public readonly AnimationController controller;
public override State createState() => new _BackdropState();
}
public class _BackdropState : State<Backdrop> {//with SingleTickerProviderStateMixin { [!!!]
public readonly GlobalKey _backdropKey = GlobalKey.key(debugLabel: "Backdrop");
AnimationController _controller;
Animation<RelativeRect> _layerAnimation;
public override void initState() {
base.initState();
_controller = widget.controller;
}
public override void dispose() {
_controller.dispose();
base.dispose();
}
bool _frontLayerVisible {
get
{
AnimationStatus status = _controller.status;
return status == AnimationStatus.completed || status == AnimationStatus.forward;
}
}
void _toggleBackdropLayerVisibility() { //[!!!]
setState(() => {
var visible = _frontLayerVisible ? _controller.reverse() : _controller.forward();
});
}
Animation<RelativeRect> _getLayerAnimation(Size layerSize, float layerTop) {
Curve firstCurve;
Curve secondCurve;
float firstWeight;
float secondWeight;
Animation<float> animation;
if (_frontLayerVisible) {
firstCurve = backdropUtils._kAccelerateCurve;
secondCurve = backdropUtils._kDecelerateCurve;
firstWeight = backdropUtils._kPeakVelocityTime;
secondWeight = 1.0f - backdropUtils._kPeakVelocityTime;
animation = new CurvedAnimation(
parent: _controller.view,
curve: new Interval(0.0f, 0.78f)
);
} else {
firstCurve = backdropUtils._kDecelerateCurve.flipped;
secondCurve = backdropUtils._kAccelerateCurve.flipped;
firstWeight = 1.0f - backdropUtils._kPeakVelocityTime;
secondWeight = backdropUtils._kPeakVelocityTime;
animation = _controller.view;
}
return new TweenSequence<RelativeRect>(
new List<TweenSequenceItem<RelativeRect>>{
new TweenSequenceItem<RelativeRect>(
tween: new RelativeRectTween(
begin: RelativeRect.fromLTRB(
0.0f,
layerTop,
0.0f,
layerTop - layerSize.height
),
end: RelativeRect.fromLTRB(
0.0f,
layerTop * backdropUtils._kPeakVelocityProgress,
0.0f,
(layerTop - layerSize.height) * backdropUtils._kPeakVelocityProgress
)
).chain(new CurveTween(curve: firstCurve)),
weight: firstWeight
),
new TweenSequenceItem<RelativeRect>(
tween: new RelativeRectTween(
begin: RelativeRect.fromLTRB(
0.0f,
layerTop * backdropUtils._kPeakVelocityProgress,
0.0f,
(layerTop - layerSize.height) * backdropUtils._kPeakVelocityProgress
),
end: RelativeRect.fill
).chain(new CurveTween(curve: secondCurve)),
weight: secondWeight
),
}
).animate(animation);
}
Widget _buildStack(BuildContext context, BoxConstraints constraints) {
float layerTitleHeight = 48.0f;
Size layerSize = constraints.biggest;
float layerTop = layerSize.height - layerTitleHeight;
_layerAnimation = _getLayerAnimation(layerSize, layerTop);
return new Stack(
key: _backdropKey,
children: new List<Widget>{
new _TappableWhileStatusIs(
AnimationStatus.dismissed,
controller: _controller,
child: widget.backLayer
),
new PositionedTransition(
rect: _layerAnimation,
child: new _FrontLayer(
onTap: _toggleBackdropLayerVisibility,
child: new _TappableWhileStatusIs(
AnimationStatus.completed,
controller: _controller,
child: widget.frontLayer
)
)
),
}
);
}
public override Widget build(BuildContext context) {
AppBar appBar = new AppBar(
brightness: Brightness.light,
elevation: 0.0f,
titleSpacing: 0.0f,
title: new _BackdropTitle(
listenable: _controller.view,
onPress: _toggleBackdropLayerVisibility,
frontTitle: widget.frontTitle,
backTitle: widget.backTitle
),
actions: new List<Widget>{
new IconButton(
icon: new Icon(Icons.search),
onPressed: () => {
Navigator.push<Void>(
context,
new MaterialPageRoute<Void>(builder: (BuildContext context) => new LoginPage())
);
}
),
new IconButton(
icon: new Icon(Icons.tune),
onPressed: () => {
Navigator.push<void>(
context,
new MaterialPageRoute<void>(builder: (BuildContext context) => new LoginPage()),
);
}
),
}
);
return new Scaffold(
appBar: appBar,
body: new LayoutBuilder(
builder: _buildStack
)
);
}
}
}

85
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/category_menu_page.cs


using System;
using System.Collections.Generic;
using UIWidgetsGallery.demo.shrine.model;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery.demo.shrine
{
public class CategoryMenuPage : StatelessWidget {
public CategoryMenuPage(
Key key = null,
VoidCallback onCategoryTap = null
) : base(key: key)
{
this.onCategoryTap = onCategoryTap;
}
public readonly VoidCallback onCategoryTap;
Widget _buildCategory(Category category, BuildContext context)
{
string categoryString = category.ToString().Replace("Category", "").ToUpper();
ThemeData theme = Theme.of(context);
return new ScopedModelDescendant<AppStateModel>(
builder: (BuildContext context2, Widget child, AppStateModel model) =>
new GestureDetector(
onTap: () =>
{
model.setCategory(category);
if (onCategoryTap != null)
{
onCategoryTap();
}
},
child: model.selectedCategory == category
? new Column(
children: new List<Widget>
{
new SizedBox(height: 16.0f),
new Text(
categoryString,
style: theme.textTheme.bodyText1,
textAlign: TextAlign.center
),
new SizedBox(height: 14.0f),
new Container(
width: 70.0f,
height: 2.0f,
color: shrineColorsUtils.kShrinePink400
),
}
)
: new Padding(
padding: EdgeInsets.symmetric(vertical: 16.0f),
child: new Text(
categoryString,
style: theme.textTheme.bodyText1.copyWith(
color: shrineColorsUtils.kShrineBrown900.withAlpha(153)
),
textAlign: TextAlign.center
)
)
)
);
}
public override Widget build(BuildContext context) {
return new Center(
child: new Container(
padding: EdgeInsets.only(top: 40.0f),
color: shrineColorsUtils.kShrinePink100,
child: new ListView(
children: Category.values.map((Category c) => _buildCategory(c, context)).toList(),
)
)
);
}
}
}

22
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/colors.cs


using uiwidgets;
using Unity.UIWidgets.ui;
namespace UIWidgetsGallery.demo.shrine
{
public class shrineColorsUtils
{
public static readonly Color kShrinePink50 = new Color(0xFFFEEAE6);
public static readonly Color kShrinePink100 = new Color(0xFFFEDBD0);
public static readonly Color kShrinePink300 = new Color(0xFFFBB8AC);
public static readonly Color kShrinePink400 = new Color(0xFFEAA4A4);
public static readonly Color kShrineBrown900 = new Color(0xFF442B2D);
public static readonly Color kShrineBrown600 = new Color(0xFF7D4F52);
public static readonly Color kShrineErrorRed = new Color(0xFFC5032B);
public static readonly Color kShrineSurfaceWhite = new Color(0xFFFFFBFA);
public static readonly Color kShrineBackgroundWhite = Colors.white;
}
}

655
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/expanding_bottom_sheet.cs


using System;
using System.Collections.Generic;
using System.Linq;
using UIWidgetsGallery.demo.shrine.model;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.async2;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery.demo.shrine
{
public class expanding_buttom_sheetUtils
{
public static readonly Cubic _kAccelerateCurve = new Cubic(0.548f, 0.0f, 0.757f, 0.464f);
public static readonly Cubic _kDecelerateCurve = new Cubic(0.23f, 0.94f, 0.41f, 1.0f);
public static readonly float _kPeakVelocityTime = 0.248210f;
public static readonly float _kPeakVelocityProgress = 0.379146f;
public static readonly float _kCartHeight = 56.0f;
public static readonly float _kCornerRadius = 24.0f;
public static readonly float _kWidthForCartIcon = 64.0f;
Animation<T> _getEmphasizedEasingAnimation<T>(
T begin,
T peak,
T end,
bool isForward,
Animation<float> parent
) {
Curve firstCurve;
Curve secondCurve;
float firstWeight;
float secondWeight;
if (isForward) {
firstCurve = _kAccelerateCurve;
secondCurve = _kDecelerateCurve;
firstWeight = _kPeakVelocityTime;
secondWeight = 1.0f - _kPeakVelocityTime;
} else {
firstCurve = _kDecelerateCurve.flipped;
secondCurve = _kAccelerateCurve.flipped;
firstWeight = 1.0f - _kPeakVelocityTime;
secondWeight = _kPeakVelocityTime;
}
return new TweenSequence<T>(
new List<TweenSequenceItem<T>>{
new TweenSequenceItem<T>(
weight: firstWeight,
tween: new Tween<T>(
begin: begin,
end: peak
).chain(new CurveTween(curve: firstCurve))
),
new TweenSequenceItem<T>(
weight: secondWeight,
tween: new Tween<T>(
begin: peak,
end: end
).chain(new CurveTween(curve: secondCurve))
),
}
).animate(parent);
}
public static float _getPeakPoint(float begin, float end) {
return begin + (end - begin) * _kPeakVelocityProgress;
}
}
public class ExpandingBottomSheet : StatefulWidget {
public ExpandingBottomSheet(Key key = null, AnimationController hideController = null)
: base(key: key)
{
D.assert(hideController != null);
this.hideController = hideController;
}
public readonly AnimationController hideController;
public override State createState() => new _ExpandingBottomSheetState();
static _ExpandingBottomSheetState of(BuildContext context, bool isNullOk = false) {
D.assert(context != null);
_ExpandingBottomSheetState result = context.findAncestorStateOfType<_ExpandingBottomSheetState>();
if (isNullOk || result != null) {
return result;
}
throw new UIWidgetsError(
"ExpandingBottomSheet.of() called with a context that does not contain a ExpandingBottomSheet.\n");
}
}
class _ExpandingBottomSheetState : State<ExpandingBottomSheet> { //with TickerProviderStateMixin {
public readonly GlobalKey _expandingBottomSheetKey = GlobalKey.key(debugLabel: "Expanding bottom sheet");
float _width = expanding_buttom_sheetUtils._kWidthForCartIcon;
AnimationController _controller;
Animation<float> _widthAnimation;
Animation<float> _heightAnimation;
Animation<float> _thumbnailOpacityAnimation;
Animation<float> _cartOpacityAnimation;
Animation<float> _shapeAnimation;
Animation<Offset> _slideAnimation;
public override void initState() {
base.initState();
_controller = new AnimationController(
duration: Duration(milliseconds: 500),
vsync: this
);
}
public override void dispose() {
_controller.dispose();
base.dispose();
}
Animation<float> _getWidthAnimation(float screenWidth) {
if (_controller.status == AnimationStatus.forward) {
return new Tween<float>(begin: _width, end: screenWidth).animate(
new CurvedAnimation(
parent: _controller.view,
curve: new Interval(0.0f, 0.3f, curve: Curves.fastOutSlowIn)
)
);
} else {
return _getEmphasizedEasingAnimation(
begin: _width,
peak: _getPeakPoint(begin: _width, end: screenWidth),
end: screenWidth,
isForward: false,
parent: new CurvedAnimation(parent: _controller.view, curve: new Interval(0.0f, 0.87f)),
);
}
}
Animation<float> _getHeightAnimation(float screenHeight) {
if (_controller.status == AnimationStatus.forward) {
return _getEmphasizedEasingAnimation(
begin: expanding_buttom_sheetUtils._kCartHeight,
peak: expanding_buttom_sheetUtils._kCartHeight + (screenHeight - expanding_buttom_sheetUtils._kCartHeight) * expanding_buttom_sheetUtils._kPeakVelocityProgress,
end: screenHeight,
isForward: true,
parent: _controller.view
);
} else {
return new Tween<float>(
begin: expanding_buttom_sheetUtils._kCartHeight,
end: screenHeight
).animate(
new CurvedAnimation(
parent: _controller.view,
curve: new Interval(0.434f, 1.0f, curve: Curves.linear),
reverseCurve: new Interval(0.434f, 1.0f, curve: Curves.fastOutSlowIn.flipped)
)
);
}
}
// Animation of the cut corner. It's cut when closed and not cut when open.
Animation<float> _getShapeAnimation() {
if (_controller.status == AnimationStatus.forward) {
return new Tween<float>(begin: expanding_buttom_sheetUtils._kCornerRadius, end: 0.0f).animate(
new CurvedAnimation(
parent: _controller.view,
curve: new Interval(0.0f, 0.3f, curve: Curves.fastOutSlowIn)
)
);
} else {
return _getEmphasizedEasingAnimation(
begin: expanding_buttom_sheetUtils._kCornerRadius,
peak: expanding_buttom_sheetUtils._getPeakPoint(begin: expanding_buttom_sheetUtils._kCornerRadius, end: 0.0f),
end: 0.0f,
isForward: false,
parent: _controller.view
);
}
}
Animation<float> _getThumbnailOpacityAnimation() {
return new Tween<float>(begin: 1.0f, end: 0.0f).animate(
new CurvedAnimation(
parent: _controller.view,
curve: _controller.status == AnimationStatus.forward
? new Interval(0.0f, 0.3f)
: new Interval(0.532f, 0.766f)
)
);
}
Animation<float> _getCartOpacityAnimation() {
return new CurvedAnimation(
parent: _controller.view,
curve: _controller.status == AnimationStatus.forward
? new Interval(0.3f, 0.6f)
: new Interval(0.766f, 1.0f)
);
}
// Returns the correct width of the ExpandingBottomSheet based on the number of
// products in the cart.
float _widthFor(int numProducts) {
switch (numProducts) {
case 0:
return expanding_buttom_sheetUtils._kWidthForCartIcon;
case 1:
return 136.0f;
case 2:
return 192.0f;
case 3:
return 248.0f;
default:
return 278.0f;
}
}
// Returns true if the cart is open or opening and false otherwise.
bool _isOpen {
get
{
AnimationStatus status = _controller.status;
return status == AnimationStatus.completed || status == AnimationStatus.forward;
}
}
// Opens the ExpandingBottomSheet if it's closed, otherwise does nothing.
void open() {
if (!_isOpen) {
_controller.forward();
}
}
// Closes the ExpandingBottomSheet if it's open or opening, otherwise does nothing.
void close() {
if (_isOpen) {
_controller.reverse();
}
}
// Changes the padding between the start edge of the Material and the cart icon
// based on the number of products in the cart (padding increases when > 0
// products.)
EdgeInsetsDirectional _cartPaddingFor(int numProducts) {
return (numProducts == 0)
? EdgeInsetsDirectional.only(start: 20.0f, end: 8.0f)
: EdgeInsetsDirectional.only(start: 32.0f, end: 8.0f);
}
bool _cartIsVisible
{
get
{
return _thumbnailOpacityAnimation.value == 0.0;
}
}
Widget _buildThumbnails(int numProducts) {
return new ExcludeSemantics(
child: new Opacity(
opacity: _thumbnailOpacityAnimation.value,
child: new Column(
children: new List<Widget>{
new Row(
children: new List<Widget>{
new AnimatedPadding(
padding: _cartPaddingFor(numProducts),
child: new Icon(Icons.shopping_cart),
duration: new Duration(milliseconds: 225)
),
new Container(
// Accounts for the overflow number
width: numProducts > 3 ? _width - 94.0f: _width - 64.0f,
height: expanding_buttom_sheetUtils._kCartHeight,
padding: EdgeInsets.symmetric(vertical: 8.0f),
child: new ProductThumbnailRow()
),
new ExtraProductsNumber(),
}
),
}
)
)
);
}
Widget _buildShoppingCartPage() {
return new Opacity(
opacity: _cartOpacityAnimation.value,
child: new ShoppingCartPage()
);
}
Widget _buildCart(BuildContext context, Widget child) {
AppStateModel model = ScopedModel.of<AppStateModel>(context);
int numProducts = model.productsInCart.Keys.Count;
int totalCartQuantity = model.totalCartQuantity;
Size screenSize = MediaQuery.of(context).size;
float screenWidth = screenSize.width;
float screenHeight = screenSize.height;
_width = _widthFor(numProducts);
_widthAnimation = _getWidthAnimation(screenWidth);
_heightAnimation = _getHeightAnimation(screenHeight);
_shapeAnimation = _getShapeAnimation();
_thumbnailOpacityAnimation = _getThumbnailOpacityAnimation();
_cartOpacityAnimation = _getCartOpacityAnimation();
return Semantics(
button: true,
value: $"Shopping cart, {totalCartQuantity} items",
child: new Container(
width: _widthAnimation.value,
height: _heightAnimation.value,
child: new Material(
animationDuration: Duration(milliseconds: 0),
shape: new BeveledRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(_shapeAnimation.value)
)
),
elevation: 4.0f,
color: shrineColorsUtils.kShrinePink50,
child: _cartIsVisible
? _buildShoppingCartPage()
: _buildThumbnails(numProducts)
)
)
);
}
// Builder for the hide and reveal animation when the backdrop opens and closes
Widget _buildSlideAnimation(BuildContext context, Widget child) {
_slideAnimation = _getEmphasizedEasingAnimation(
begin: new Offset(1.0f, 0.0f),
peak: new Offset(expanding_buttom_sheetUtils._kPeakVelocityProgress, 0.0f),
end: new Offset(0.0f, 0.0f),
isForward: widget.hideController.status == AnimationStatus.forward,
parent: widget.hideController
);
return new SlideTransition(
position: _slideAnimation,
child: child
);
}
// Closes the cart if the cart is open, otherwise exits the app (this should
// only be relevant for Android).
Future<bool> _onWillPop() async {
if (!_isOpen) {
await SystemNavigator.pop();
return true;
}
close();
return true;
}
public override Widget build(BuildContext context) {
return new AnimatedSize(
key: _expandingBottomSheetKey,
duration: Duration(milliseconds: 225),
curve: Curves.easeInOut,
vsync: this,
alignment: FractionalOffset.topLeft,
child: new WillPopScope(
onWillPop: _onWillPop,
child: new AnimatedBuilder(
animation: widget.hideController,
builder: _buildSlideAnimation,
child: new GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: open,
child: new ScopedModelDescendant<AppStateModel>(
builder: (BuildContext context2, Widget child, AppStateModel model)=> {
return new AnimatedBuilder(
builder: _buildCart,
animation: _controller
);
}
)
)
)
)
);
}
}
public class ProductThumbnailRow : StatefulWidget {
public override State createState() => new _ProductThumbnailRowState();
}
public class _ProductThumbnailRowState : State<ProductThumbnailRow> {
public readonly GlobalKey<AnimatedListState> _listKey = new GlobalKey<AnimatedListState>();
_ListModel _list;
List<int> _internalList;
public override void initState() {
base.initState();
_list = new _ListModel(
listKey: _listKey,
initialItems: ScopedModel.of<AppStateModel>(context).productsInCart.keys.toList(),
removedItemBuilder: _buildRemovedThumbnail
);
_internalList = new List<int>(_list.list);
}
Product _productWithId(int productId) {
AppStateModel model = ScopedModel.of<AppStateModel>(context);
Product product = model.getProductById(productId);
D.assert(product != null);
return product;
}
Widget _buildRemovedThumbnail(int item, BuildContext context, Animation<float> animation) {
return new ProductThumbnail(animation, animation, _productWithId(item));
}
Widget _buildThumbnail(BuildContext context, int index, Animation<float> animation) {
Animation<float> thumbnailSize = new Tween<float>(begin: 0.8f, end: 1.0f).animate(
new CurvedAnimation(
curve: new Interval(0.33f, 1.0f, curve: Curves.easeIn),
parent: animation
)
);
Animation<float> opacity = new CurvedAnimation(
curve: new Interval(0.33f, 1.0f, curve: Curves.linear),
parent: animation
);
return new ProductThumbnail(thumbnailSize, opacity, _productWithId(_list[index]));
}
// If the lists are the same length, assume nothing has changed.
// If the internalList is shorter than the ListModel, an item has been removed.
// If the internalList is longer, then an item has been added.
void _updateLists() {
// Update _internalList based on the model
_internalList = ScopedModel.of<AppStateModel>(context).productsInCart.keys.toList();
HashSet<int> internalSet = new HashSet<int>(_internalList);
HashSet<int> listSet = new HashSet<int>(_list.list);
HashSet<int> difference = null;
foreach (var _set in internalSet)
{
if (!listSet.Contains(_set))
{
difference.Add(_set);
}
}
if (difference.isEmpty()) {
return;
}
foreach (int product in difference) {
if (_internalList.Count < _list.length) {
_list.remove(product);
} else if (_internalList.Count > _list.length) {
_list.add(product);
}
}
while (_internalList.Count != _list.length) {
int index = 0;
// Check bounds and that the list elements are the same
while (_internalList.isNotEmpty &&
_list.length > 0 &&
index < _internalList.Count &&
index < _list.length &&
_internalList[index] == _list[index]) {
index++;
}
}
}
Widget _buildAnimatedList() {
return new AnimatedList(
key: _listKey,
shrinkWrap: true,
itemBuilder: _buildThumbnail,
initialItemCount: _list.length,
scrollDirection: Axis.horizontal,
physics: new NeverScrollableScrollPhysics() // Cart shouldn't scroll
);
}
public override Widget build(BuildContext context) {
_updateLists();
return new ScopedModelDescendant<AppStateModel>(
builder: (BuildContext context2, Widget child, AppStateModel model) => _buildAnimatedList()
);
}
}
public class ExtraProductsNumber : StatelessWidget {
int _calculateOverflow(AppStateModel model) {
Dictionary<int, int> productMap = model.productsInCart;
List<int> products = productMap.Keys.ToList();
int overflow = 0;
int numProducts = products.Count;
if (numProducts > 3) {
for (int i = 3; i < numProducts; i++) {
overflow += productMap[products[i]];
}
}
return overflow;
}
Widget _buildOverflow(AppStateModel model, BuildContext context) {
if (model.productsInCart.Count <= 3)
return new Container();
int numOverflowProducts = _calculateOverflow(model);
int displayedOverflowProducts = numOverflowProducts <= 99 ? numOverflowProducts : 99;
return new Container(
child: new Text(
$"+{displayedOverflowProducts}",
style: Theme.of(context).primaryTextTheme.button
)
);
}
public override Widget build(BuildContext context) {
return new ScopedModelDescendant<AppStateModel>(
builder: (BuildContext builder, Widget child, AppStateModel model) => _buildOverflow(model, context),
);
}
}
public class ProductThumbnail : StatelessWidget {
public ProductThumbnail(Animation<float> animation, Animation<float> opacityAnimation, Product product)
{
this.animation = animation;
this.opacityAnimation = opacityAnimation;
this.product = product;
}
public readonly Animation<float> animation;
public readonly Animation<float> opacityAnimation;
public readonly Product product;
public override Widget build(BuildContext context) {
return new FadeTransition(
opacity: opacityAnimation,
child: new ScaleTransition(
scale: animation,
child: new Container(
width: 40.0f,
height: 40.0f,
decoration: new BoxDecoration(
image: new DecorationImage(
image: new ExactAssetImage(
product.assetName,
package: product.assetPackage
),
fit: BoxFit.cover
),
borderRadius: BorderRadius.all(Radius.circular(10.0f))
),
margin: EdgeInsets.only(left: 16.0f)
)
)
);
}
}
public class _ListModel {
public _ListModel(
GlobalKey<AnimatedListState> listKey,
Delegate removedItemBuilder,
IEnumerable<int> initialItems
)
{
D.assert(listKey != null);
D.assert(removedItemBuilder != null);
_items = initialItems?.ToList() ?? new List<int>();
}
public readonly GlobalKey<AnimatedListState> listKey;
//public readonly Delegate removedItemBuilder;
public delegate Widget removedItemBuilder(int item, BuildContext context, Animation<float> animation);
public readonly List<int> _items;
AnimatedListState _animatedList
{
get
{
return listKey.currentState;
}
}
public void add(int product) {
_insert(_items.Count, product);
}
public void _insert(int index, int item) {
_items.Insert(index, item);
_animatedList.insertItem(index, duration: Duration(milliseconds: 225));
}
public void remove(int product) {
int index = _items.IndexOf(product);
if (index >= 0) {
_removeAt(index);
}
}
public void _removeAt(int index) {
_items.RemoveAt(index);
_animatedList.removeItem(index, (BuildContext context, Animation<float> animation) =>{
return removedItemBuilder(removedItem, context, animation);
});
}
public int length
{
get
{
return _items.Count;
}
}
int operator [](int index) => _items[index];
int indexOf(int item) => _items.IndexOf(item);
public List<int> list
{
get
{
return _items;
}
}
}
}

53
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/home.cs


using System.Collections.Generic;
using UIWidgetsGallery.demo.shrine.model;
using UIWidgetsGallery.gallery;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery.demo.shrine
{
public class ProductPage : StatelessWidget {
public ProductPage(Category category = Category.all)
{
this.category = category;
}
public readonly Category category;
public override Widget build(BuildContext context) {
return new ScopedModelDescendant<AppStateModel>(
builder: (BuildContext context, Widget child, AppStateModel model) => {
return AsymmetricView(products: model.getProducts());
});
}
}
public class HomePage : StatelessWidget {
public HomePage(
ExpandingBottomSheet expandingBottomSheet = null,
Backdrop backdrop = null,
Key key = null
) : base(key: key)
{
this.expandingBottomSheet = expandingBottomSheet;
this.backdrop = backdrop;
}
public readonly ExpandingBottomSheet expandingBottomSheet;
public readonly Backdrop backdrop;
public override Widget build(BuildContext context) {
return new Stack(
children: new List<Widget>
{
backdrop,
new Align(child: expandingBottomSheet, alignment: Alignment.bottomRight),
}
);
}
}
}

136
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/login.cs


using System.Collections.Generic;
using uiwidgets;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using Image = Unity.UIWidgets.widgets.Image;
namespace UIWidgetsGallery.demo.shrine
{
public class LoginPage : StatefulWidget {
public override State createState() => new _LoginPageState();
}
public class _LoginPageState : State<LoginPage> {
public readonly TextEditingController _usernameController = new TextEditingController();
public readonly TextEditingController _passwordController = new TextEditingController();
static ShapeDecoration _decoration = new ShapeDecoration(
shape: new BeveledRectangleBorder(
side: new BorderSide(color: shrineColorsUtils.kShrineBrown900, width: 0.5f),
borderRadius: BorderRadius.all(Radius.circular(7.0f))
)
);
public override Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
elevation: 0.0f,
backgroundColor: Colors.white,
brightness: Brightness.light,
leading: new IconButton(
icon: new BackButtonIcon(),
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
onPressed: () => {
Navigator.of(context, rootNavigator: true).pop();
}
)
),
body: new SafeArea(
child: new ListView(
padding: EdgeInsets.symmetric(horizontal: 24.0f),
children: new List<Widget>{
new SizedBox(height: 80.0f),
new Column(
children: new List<Widget>
{
Image.asset("packages/shrine_images/diamond.png"),
new SizedBox(height: 16.0f),
new Text(
"SHRINE",
style: Theme.of(context).textTheme.headline5
),
}
),
new SizedBox(height: 120.0f),
new PrimaryColorOverride(
color: shrineColorsUtils.kShrineBrown900,
child: new Container(
decoration: _decoration,
child: new TextField(
controller: _usernameController,
decoration: new InputDecoration(
labelText: "Username"
)
)
)
),
new SizedBox(height: 12.0f),
new PrimaryColorOverride(
color: shrineColorsUtils.kShrineBrown900,
child: new Container(
decoration: _decoration,
child: new TextField(
controller: _passwordController,
decoration: new InputDecoration(
labelText: "Password"
)
)
)
),
new Wrap(
children: new List<Widget>{
new ButtonBar(
children:new List<Widget>{
new FlatButton(
child: new Text("CANCEL"),
shape: new BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0f))
),
onPressed: () => {
Navigator.of(context, rootNavigator: true).pop();
}
),
new RaisedButton(
child: new Text("NEXT"),
elevation: 8.0f,
shape: new BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0f))
),
onPressed: () => {
Navigator.pop(context);
}
)
}
),
}
),
}
)
)
);
}
}
public class PrimaryColorOverride : StatelessWidget {
public PrimaryColorOverride(Key key = null, Color color = null, Widget child = null) : base(key: key)
{
this.child = child;
this.color = color;
}
public readonly Color color;
public readonly Widget child;
public override Widget build(BuildContext context) {
return new Theme(
child: child,
data: Theme.of(context).copyWith(primaryColor: color)
);
}
}
}

283
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/shopping_cart.cs


using System.Collections.Generic;
using UIWidgetsGallery.demo.shrine.model;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using Image = Unity.UIWidgets.widgets.Image;
namespace UIWidgetsGallery.demo.shrine
{
public class shopping_cartUtils
{
public static readonly float _leftColumnWidth = 60.0f;
}
class ShoppingCartPage : StatefulWidget {
public override State createState() => new _ShoppingCartPageState();
}
public class _ShoppingCartPageState : State<ShoppingCartPage> {
List<Widget> _createShoppingCartRows(AppStateModel model) {
return model.productsInCart.Keys
.map((int id) => new ShoppingCartRow(
product: model.getProductById(id),
quantity: model.productsInCart[id],
onPressed: () =>{
model.removeItemFromCart(id);
}
)
)
.toList();
}
public override Widget build(BuildContext context) {
ThemeData localTheme = Theme.of(context);
return new Scaffold(
backgroundColor: shrineColorsUtils.kShrinePink50,
body: new SafeArea(
child: new Container(
child: new ScopedModelDescendant<AppStateModel>(
builder: (BuildContext context2, Widget child, AppStateModel model) => {
return new Stack(
children: new List<Widget>{
new ListView(
children: new List<Widget>{
new Row(
children: new List<Widget>{
new SizedBox(
width: shopping_cartUtils._leftColumnWidth,
child: new IconButton(
icon: new Icon(Icons.keyboard_arrow_down),
onPressed: () => ExpandingBottomSheet.of(context).close()
)
),
new Text(
"CART",
style: localTheme.textTheme.subtitle1.copyWith(fontWeight: FontWeight.w600)
),
new SizedBox(width: 16.0f),
new Text($"{model.totalCartQuantity} ITEMS")
}
),
new SizedBox(height: 16.0f),
new Column(
children: _createShoppingCartRows(model)
),
new ShoppingCartSummary(model: model),
new SizedBox(height: 100.0f)
}
),
new Positioned(
bottom: 16.0f,
left: 16.0f,
right: 16.0f,
child: new RaisedButton(
shape: new BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0f))
),
color: shrineColorsUtils.kShrinePink100,
splashColor: shrineColorsUtils.kShrineBrown600,
child: new Padding(
padding: EdgeInsets.symmetric(vertical: 12.0f),
child: new Text("CLEAR CART")
),
onPressed: () => {
model.clearCart();
ExpandingBottomSheet.of(context).close();
}
)
)
}
);
}
)
)
)
);
}
}
public class ShoppingCartSummary : StatelessWidget {
public ShoppingCartSummary(AppStateModel model)
{
this.model = model;
}
public readonly AppStateModel model;
public override Widget build(BuildContext context) {
TextStyle smallAmountStyle = Theme.of(context).textTheme.bodyText2.copyWith(color: shrineColorsUtils.kShrineBrown600);
TextStyle largeAmountStyle = Theme.of(context).textTheme.headline4;
NumberFormat formatter = NumberFormat.simpleCurrency(
decimalDigits: 2,
locale: Localizations.localeOf(context).ToString()
);
return new Row(
children: new List<Widget>
{
new SizedBox(width: shopping_cartUtils._leftColumnWidth),
new Expanded(
child: new Padding(
padding: EdgeInsets.only(right: 16.0f),
child: new Column(
children: new List<Widget>
{
new Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: new List<Widget>
{
new Expanded(
child: new Text("TOTAL")
),
new Text(
formatter.format(model.totalCost),
style: largeAmountStyle
)
}
),
new SizedBox(height: 16.0f),
new Row(
children: new List<Widget>
{
new Expanded(
child: new Text("Subtotal:")
),
new Text(
formatter.format(model.subtotalCost),
style: smallAmountStyle
)
}
),
new SizedBox(height: 4.0f),
new Row(
children: new List<Widget>
{
new Expanded(
child: new Text("Shipping:")
),
new Text(
formatter.format(model.shippingCost),
style: smallAmountStyle
)
}
),
new SizedBox(height: 4.0f),
new Row(
children: new List<Widget>
{
new Expanded(
child: new Text("Tax:")
),
new Text(
formatter.format(model.tax),
style: smallAmountStyle
)
}
)
}
)
)
)
}
);
}
}
public class ShoppingCartRow : StatelessWidget {
public ShoppingCartRow(
Product product = null,
int? quantity = null,
VoidCallback onPressed = null
)
{
this.product = product;
this.quantity = quantity?? 0;
this.onPressed = onPressed;
}
public readonly Product product;
public readonly int quantity;
public readonly VoidCallback onPressed;
public override Widget build(BuildContext context) {
NumberFormat formatter = NumberFormat.simpleCurrency(
decimalDigits: 0,
locale: Localizations.localeOf(context).ToString()
);
ThemeData localTheme = Theme.of(context);
return new Padding(
padding: EdgeInsets.only(bottom: 16.0f),
child: new Row(
key: ValueKey<int>(product.id),
crossAxisAlignment: CrossAxisAlignment.start,
children: new List<Widget>{
new SizedBox(
width: shopping_cartUtils._leftColumnWidth,
child: new IconButton(
icon: new Icon(Icons.remove_circle_outline),
onPressed: onPressed
)
),
new Expanded(
child: new Padding(
padding: EdgeInsets.only(right: 16.0f),
child: new Column(
children: new List<Widget>{
new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: new List<Widget>{
Image.asset(
product.assetName,
package: product.assetPackage,
fit: BoxFit.cover,
width: 75.0f,
height: 75.0f
),
new SizedBox(width: 16.0f),
new Expanded(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: new List<Widget>{
new Row(
children: new List<Widget>{
new Expanded(
child: new Text($"Quantity: {quantity}"),
),
new Text($"x {formatter.format(product.price)}")
}
),
new Text(
product.name,
style: localTheme.textTheme.subtitle1.copyWith(fontWeight: FontWeight.w600)
)
}
)
)
}
),
new SizedBox(height: 16.0f),
new Divider(
color: shrineColorsUtils.kShrineBrown900,
height: 10.0f
)
}
)
)
)
}
)
);
}
}
}

81
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/supplemental/asymmetric_view.cs


using System.Collections.Generic;
using System.Linq;
using UIWidgetsGallery.demo.shrine.model;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery.demo.shrine.supplemental
{
public class AsymmetricView : StatelessWidget {
public AsymmetricView(Key key = null, List<Product> products = null) : base(key: key)
{
this.products = products;
}
public readonly List<Product> products;
List<Container> _buildColumns(BuildContext context) {
if (products == null || products.isEmpty()) {
return new List<Container>();
}
List<Container> list = new List<Container>();
var len = _listItemCount(products.Count());
for (int index = 0; index < len; index++)
{
float width = 0.59f * MediaQuery.of(context).size.width;
Widget column;
if (index % 2 == 0) {
int bottom = _evenCasesIndex(index);
column = new TwoProductCardColumn(
bottom: products[bottom],
top: products.Count - 1 >= bottom + 1
? products[bottom + 1]
: null
);
width += 32.0f;
} else {
column = new OneProductCardColumn(
product: products[_oddCasesIndex(index)]
);
}
list.Add(new Container(
width: width,
child: new Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0f),
child: column
)
));
}
return list;
}
int _evenCasesIndex(int input) {
return input / 2 * 3;
}
int _oddCasesIndex(int input) {
D.assert(input > 0);
return (input / 2) * 3 - 1;
}
int _listItemCount(int totalItems) {
return (totalItems % 3 == 0)
? totalItems / 3 * 2
: (totalItems / 3) * 2 - 1;
}
public override Widget build(BuildContext context) {
return new ListView(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.fromLTRB(0.0f, 34.0f, 16.0f, 44.0f),
children: _buildColumns(context),
physics: new AlwaysScrollableScrollPhysics()
);
}
}
}

137
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/supplemental/cut_corners_border.cs


using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
namespace UIWidgetsGallery.demo.shrine.supplemental
{
public class CutCornersBorder : OutlineInputBorder {
public CutCornersBorder(
BorderSide borderSide = null,
BorderRadius borderRadius = null,
float cut = 7.0f,
float gapPadding = 2.0f
) : base(
borderSide: borderSide?? BorderSide.none,
borderRadius: borderRadius?? BorderRadius.all(Radius.circular(2.0f)),
gapPadding: gapPadding
)
{
this.cut = cut;
}
public CutCornersBorder copyWith(
BorderSide borderSide = null,
BorderRadius borderRadius = null,
float? gapPadding = null,
float? cut = null
) {
return new CutCornersBorder(
borderSide: borderSide ?? this.borderSide,
borderRadius: borderRadius ?? this.borderRadius,
gapPadding: gapPadding ?? this.gapPadding,
cut: cut ?? this.cut
);
}
public readonly float cut;
public override ShapeBorder lerpFrom(ShapeBorder a, float t) {
if (a is CutCornersBorder) {
CutCornersBorder outline = (CutCornersBorder)a;
return new CutCornersBorder(
borderRadius: BorderRadius.lerp(outline.borderRadius, borderRadius, t),
borderSide: BorderSide.lerp(outline.borderSide, borderSide, t),
cut: cut,
gapPadding: outline.gapPadding
);
}
return base.lerpFrom(a, t);
}
public override ShapeBorder lerpTo(ShapeBorder b, float t) {
if (b is CutCornersBorder) {
CutCornersBorder outline = (CutCornersBorder)b;
return new CutCornersBorder(
borderRadius: BorderRadius.lerp(borderRadius, outline.borderRadius, t),
borderSide: BorderSide.lerp(borderSide, outline.borderSide, t),
cut: cut,
gapPadding: outline.gapPadding
);
}
return base.lerpTo(b, t);
}
Path _notchedCornerPath(Rect center, float start = 0.0f, float extent = 0.0f) {
Path path = new Path();
if (start > 0.0 || extent > 0.0) {
path.relativeMoveTo(extent + start, center.top);
_notchedSidesAndBottom(center, path);
path.lineTo(center.left + cut, center.top);
path.lineTo(start, center.top);
} else {
path.moveTo(center.left + cut, center.top);
_notchedSidesAndBottom(center, path);
path.lineTo(center.left + cut, center.top);
}
return path;
}
Path _notchedSidesAndBottom(Rect center, Path path)
{
path.lineTo(center.right - cut, center.top);
path.lineTo(center.right, center.top + cut);
path.lineTo(center.right, center.top + center.height - cut);
path.lineTo(center.right - cut, center.top + center.height);
path.lineTo(center.left + cut, center.top + center.height);
path.lineTo(center.left, center.top + center.height - cut);
path.lineTo(center.left, center.top + cut);
return path;
}
public void paint(
Canvas canvas,
Rect rect,
float gapStart,
float gapExtent = 0.0f,
float gapPercentage = 0.0f,
TextDirection? textDirection = null
) {
D.assert(gapPercentage >= 0.0 && gapPercentage <= 1.0);
Paint paint = borderSide.toPaint();
RRect outer = borderRadius.toRRect(rect);
if (gapStart == null || gapExtent <= 0.0 || gapPercentage == 0.0) {
canvas.drawPath(_notchedCornerPath(outer.middleRect), paint);
} else {
float extent = lerpFloat(0.0f, gapExtent + gapPadding * 2.0f, gapPercentage)?? 0.0f;
switch (textDirection) {
case TextDirection.rtl: {
Path path = _notchedCornerPath(outer.middleRect, gapStart + gapPadding - extent, extent);
canvas.drawPath(path, paint);
break;
}
case TextDirection.ltr: {
Path path = _notchedCornerPath(outer.middleRect, gapStart - gapPadding, extent);
canvas.drawPath(path, paint);
break;
}
}
}
}
float? lerpFloat(float? a, float? b, float t) {
if (a == null && b == null)
return null;
a = a?? 0.0f;
b = b?? 0.0f;
return a + (b - a) * t;
}
}
}

102
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/supplemental/product_card.cs


using System.Collections.Generic;
using UIWidgetsGallery.demo.shrine.model;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery.demo.shrine.supplemental
{
public static class product_cardutils
{
public static readonly float kTextBoxHeight = 65.0f;
}
public class ProductCard : StatelessWidget {
public ProductCard(float imageAspectRatio = 33f / 49f, Product product = null)
{
D.assert(imageAspectRatio > 0);
this.imageAspectRatio = imageAspectRatio;
this.product = product;
}
public readonly float imageAspectRatio;
public readonly Product product;
public override Widget build(BuildContext context) {
NumberFormat formatter = NumberFormat.simpleCurrency(
decimalDigits: 0,
locale: Localizations.localeOf(context).ToString()
);
ThemeData theme = Theme.of(context);
Image imageWidget = Image.asset(
product.assetName,
package: product.assetPackage,
fit: BoxFit.cover
);
return new ScopedModelDescendant<AppStateModel>(
builder: (BuildContext context2, Widget child, AppStateModel model) =>
{
return new GestureDetector(
onTap: () =>
{
model.addProductToCart(product.id);
},
child: child
);
},
child: new Stack(
children: new List<Widget>
{
new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: new List<Widget>
{
new AspectRatio(
aspectRatio: imageAspectRatio,
child: imageWidget
),
new SizedBox(
height: product_cardutils.kTextBoxHeight * MediaQuery.of(context).textScaleFactor,
width: 121.0f,
child: new Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: new List<Widget>
{
new Text(
product == null ? "" : product.name,
style: theme.textTheme.button,
softWrap: false,
overflow: TextOverflow.ellipsis,
maxLines: 1
),
new SizedBox(height: 4.0f),
new Text(
product == null ? "" : formatter.format(product.price),
style: theme.textTheme.caption
),
}
)
)
}
),
new Padding(
padding: EdgeInsets.all(16.0f),
child: new Icon(Icons.add_shopping_cart)
),
}
)
);
}
}
}

85
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/supplemental/product_columns.cs


using System.Collections.Generic;
using UIWidgetsGallery.demo.shrine.model;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery.demo.shrine.supplemental
{
public class TwoProductCardColumn : StatelessWidget {
public TwoProductCardColumn(Product bottom = null, Product top = null)
{
D.assert(bottom != null);
this.bottom = bottom;
this.top = top;
}
public readonly Product bottom, top;
public override Widget build(BuildContext context) {
return new LayoutBuilder(builder: (BuildContext context1, BoxConstraints constraints) => {
float spacerHeight = 44.0f;
float heightOfCards = (constraints.biggest.height - spacerHeight) / 2.0f;
float availableHeightForImages = heightOfCards - product_cardutils.kTextBoxHeight;
float imageAspectRatio = availableHeightForImages >= 0.0
? constraints.biggest.width / availableHeightForImages
: 49.0f / 33.0f;
return new ListView(
physics:new ClampingScrollPhysics(),
children: new List<Widget>
{
new Padding(
padding: EdgeInsets.only(left: 28.0f),
child: top != null
? new ProductCard(
imageAspectRatio: imageAspectRatio,
product: top
)
: new SizedBox(
height: heightOfCards > 0 ? heightOfCards : spacerHeight
)
),
new SizedBox(height: spacerHeight),
new Padding(
padding: EdgeInsets.only(right: 28.0f),
child: new ProductCard(
imageAspectRatio: imageAspectRatio,
product: bottom
)
),
}
);
});
}
}
public class OneProductCardColumn : StatelessWidget {
public OneProductCardColumn(Product product = null)
{
this.product = product;
}
public readonly Product product;
public override Widget build(BuildContext context) {
return new ListView(
physics: new ClampingScrollPhysics(),
reverse: true,
children: new List<Widget>
{
new SizedBox(
height: 40.0f
),
new ProductCard(
product: product
),
}
);
}
}
}

/Samples/UIWidgetsSamples_2019_4/Assets/test.png → /Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/logo.png

正在加载...
取消
保存