guanghuispark
4 年前
当前提交
a9d99233
共有 153 个文件被更改,包括 12176 次插入 和 16 次删除
-
4Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_navigation_demo.cs
-
2Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/model/app_state_model.cs
-
4Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/model/product.cs
-
7Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/model/scoped_model.cs
-
2Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/gallery/backdrop.cs
-
9Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/gallery/demos.cs
-
4com.unity.uiwidgets/Runtime/animation/tween_sequence.cs
-
2com.unity.uiwidgets/Runtime/material/button_theme.cs
-
2com.unity.uiwidgets/Runtime/painting/image_provider.cs
-
4com.unity.uiwidgets/Runtime/widgets/animated_list.cs
-
2com.unity.uiwidgets/Runtime/widgets/heroes.cs
-
5com.unity.uiwidgets/Runtime/widgets/navigator.cs
-
130Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/app.cs
-
421Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/backdrop.cs
-
95Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/category_menu_page.cs
-
22Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/colors.cs
-
697Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/expanding_bottom_sheet.cs
-
54Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/home.cs
-
136Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/login.cs
-
283Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine/shopping_cart.cs
-
15Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine_demo.cs
-
3Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine_demo.cs.meta
-
69Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/0-0.jpg
-
69Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/1-0.jpg
-
45Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/10-0.jpg
-
48Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/11-0.jpg
-
55Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/12-0.jpg
-
57Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/13-0.jpg
-
47Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/14-0.jpg
-
59Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/15-0.jpg
-
51Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/16-0.jpg
-
37Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/17-0.jpg
-
62Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/18-0.jpg
-
51Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/19-0.jpg
-
59Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2-0.jpg
-
211Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/0-0.jpg
-
146Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/1-0.jpg
-
144Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/10-0.jpg
-
129Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/11-0.jpg
-
173Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/12-0.jpg
-
126Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/13-0.jpg
-
116Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/14-0.jpg
-
178Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/15-0.jpg
-
138Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/16-0.jpg
-
105Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/17-0.jpg
-
211Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/18-0.jpg
-
167Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/19-0.jpg
-
162Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/2-0.jpg
-
132Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/20-0.jpg
-
191Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/21-0.jpg
-
168Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/22-0.jpg
-
128Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/23-0.jpg
-
143Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/24-0.jpg
-
150Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/25-0.jpg
-
144Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/26-0.jpg
-
174Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/27-0.jpg
-
122Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/28-0.jpg
-
198Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/29-0.jpg
-
147Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/3-0.jpg
-
160Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/30-0.jpg
-
146Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/31-0.jpg
-
194Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/32-0.jpg
-
148Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/33-0.jpg
-
189Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/34-0.jpg
-
220Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/35-0.jpg
-
173Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/36-0.jpg
-
181Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/37-0.jpg
-
146Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/4-0.jpg
-
126Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/5-0.jpg
-
155Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/6-0.jpg
-
130Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/7-0.jpg
-
136Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/8-0.jpg
-
137Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/9-0.jpg
-
9Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/diamond.png
-
4Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/2.0x/slanted_menu.png
-
52Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/20-0.jpg
-
51Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/21-0.jpg
-
56Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/22-0.jpg
-
51Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/23-0.jpg
-
62Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/24-0.jpg
-
61Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/25-0.jpg
-
54Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/26-0.jpg
-
60Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/27-0.jpg
-
45Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/28-0.jpg
-
75Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/29-0.jpg
-
49Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/3-0.jpg
-
437Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/3.0x/0-0.jpg
-
263Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/3.0x/1-0.jpg
-
290Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/3.0x/10-0.jpg
-
218Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/3.0x/11-0.jpg
-
290Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/3.0x/12-0.jpg
-
222Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/3.0x/13-0.jpg
-
247Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/3.0x/14-0.jpg
-
492Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/3.0x/15-0.jpg
-
317Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/3.0x/16-0.jpg
-
231Samples/UIWidgetsSamples_2019_4/Assets/StreamingAssets/shrine_images/3.0x/17-0.jpg
|
|||
using System; |
|||
using UIWidgetsGallery.demo.shrine; |
|||
using UIWidgetsGallery.demo.shrine.supplemental; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.material; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
|
|||
namespace UIWidgetsGallery.demo.shrine |
|||
{ |
|||
|
|||
public class appUtils |
|||
{ |
|||
public static Route _getRoute(RouteSettings settings) { |
|||
if (settings.name != "/login") { |
|||
return null; |
|||
} |
|||
|
|||
return new MaterialPageRoute( |
|||
settings: settings, |
|||
builder: (BuildContext context) => new LoginPage(), |
|||
fullscreenDialog: true |
|||
); |
|||
} |
|||
|
|||
public static readonly ThemeData _kShrineTheme = _buildShrineTheme(); |
|||
|
|||
static IconThemeData _customIconTheme(IconThemeData original) { |
|||
return original.copyWith(color: shrineColorsUtils.kShrineBrown900); |
|||
} |
|||
|
|||
static 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: new CutCornersBorder()), |
|||
textTheme: _buildShrineTextTheme(_base.textTheme), |
|||
primaryTextTheme: _buildShrineTextTheme(_base.primaryTextTheme), |
|||
accentTextTheme: _buildShrineTextTheme(_base.accentTextTheme), |
|||
iconTheme: _customIconTheme(_base.iconTheme) |
|||
); |
|||
} |
|||
|
|||
static 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 |
|||
); |
|||
} |
|||
|
|||
static 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 |
|||
); |
|||
|
|||
|
|||
} |
|||
public class ShrineApp : StatefulWidget { |
|||
public override State createState() => new _ShrineAppState(); |
|||
} |
|||
|
|||
public class _ShrineAppState : SingleTickerProviderStateMixin<ShrineApp> { |
|||
|
|||
AnimationController _controller; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
_controller = new AnimationController( |
|||
vsync: this, |
|||
duration: TimeSpan.FromMilliseconds(450), |
|||
value: 1.0f |
|||
); |
|||
} |
|||
|
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new MaterialApp( |
|||
title: "Shrine", |
|||
home: new HomePage( |
|||
backdrop: new Backdrop( |
|||
frontLayer: new ProductPage(), |
|||
backLayer: new CategoryMenuPage(onCategoryTap: () => _controller.forward()), |
|||
frontTitle: new Text("SHRINE"), |
|||
backTitle: new Text("MENU"), |
|||
controller: _controller |
|||
), |
|||
expandingBottomSheet: new ExpandingBottomSheet(hideController: _controller) |
|||
), |
|||
initialRoute: "/login", |
|||
onGenerateRoute: appUtils._getRoute, |
|||
theme: appUtils._kShrineTheme.copyWith(platform: Theme.of(context).platform) |
|||
); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
} |
|
|||
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?.Invoke();}, |
|||
child: new Container( |
|||
height: 40.0f, |
|||
alignment: AlignmentDirectional.centerStart |
|||
) |
|||
), |
|||
new Expanded( |
|||
child: child |
|||
), |
|||
} |
|||
) |
|||
); |
|||
} |
|||
} |
|||
public delegate void OnPress(); |
|||
|
|||
public class _BackdropTitle : AnimatedWidget { |
|||
public _BackdropTitle( |
|||
Key key = null, |
|||
Animation<float> listenable = null, |
|||
OnPress onPress = null, |
|||
Widget frontTitle = null, |
|||
Widget backTitle = null |
|||
) : base(key: key, listenable: listenable) |
|||
{ |
|||
D.assert(frontTitle != null); |
|||
D.assert(backTitle != null); |
|||
this.backTitle = backTitle; |
|||
this.frontTitle = frontTitle; |
|||
this.onPress = onPress; |
|||
} |
|||
|
|||
public readonly OnPress 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?.Invoke();}, |
|||
icon: new Stack(children: new List<Widget>{ |
|||
new Opacity( |
|||
opacity: animation.value, |
|||
child: new Container( |
|||
width: 44.0f, |
|||
height: 44.0f, |
|||
decoration: new BoxDecoration( |
|||
image: new DecorationImage( |
|||
image: new FileImage( |
|||
file: "logo.png" |
|||
) |
|||
), |
|||
shape: BoxShape.circle |
|||
) |
|||
) |
|||
), |
|||
new FractionalTranslation( |
|||
translation: new OffsetTween( |
|||
begin: Offset.zero, |
|||
end: new Offset(1.0f, 0.0f)).evaluate(animation), |
|||
child: new Container( |
|||
width: 44.0f, |
|||
height: 44.0f, |
|||
decoration: new BoxDecoration( |
|||
image: new DecorationImage( |
|||
image: new FileImage( |
|||
file: "logo.png" |
|||
) |
|||
), |
|||
shape: BoxShape.circle |
|||
) |
|||
) |
|||
) |
|||
}) |
|||
) |
|||
), |
|||
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 OffsetTween( |
|||
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 OffsetTween( |
|||
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 : SingleTickerProviderStateMixin<Backdrop> { |
|||
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<object>( |
|||
context, |
|||
new MaterialPageRoute(builder: (BuildContext context2) => new LoginPage()) |
|||
); |
|||
} |
|||
), |
|||
new IconButton( |
|||
icon: new Icon(Icons.tune), |
|||
onPressed: () => { |
|||
Navigator.push<object>( |
|||
context, |
|||
new MaterialPageRoute(builder: (BuildContext context2) => new LoginPage()) |
|||
); |
|||
} |
|||
), |
|||
} |
|||
); |
|||
return new Scaffold( |
|||
appBar: appBar, |
|||
body: new LayoutBuilder( |
|||
builder: _buildStack |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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 Container( |
|||
child: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 Container( |
|||
child: 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) { |
|||
var count = Enum.GetNames(typeof(Category)).Length; |
|||
List<Widget> widgets = new List<Widget>(); |
|||
for (int i = 0;i<count;i++) |
|||
{ |
|||
widgets.Add( _buildCategory((Category)i, context)); |
|||
} |
|||
return new Center( |
|||
child: new Container( |
|||
padding: EdgeInsets.only(top: 40.0f), |
|||
color: shrineColorsUtils.kShrinePink100, |
|||
child: new ListView( |
|||
children: widgets |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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; |
|||
|
|||
} |
|||
} |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Xml.Serialization; |
|||
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; |
|||
|
|||
public static Animation<float> _getFloatEmphasizedEasingAnimation( |
|||
float begin, |
|||
float peak, |
|||
float 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<float>( |
|||
new List<TweenSequenceItem<float>>{ |
|||
new TweenSequenceItem<float>( |
|||
weight: firstWeight, |
|||
tween: new FloatTween( |
|||
begin: begin, |
|||
end: peak |
|||
).chain(new CurveTween(curve: firstCurve)) |
|||
), |
|||
new TweenSequenceItem<float>( |
|||
weight: secondWeight, |
|||
tween: new FloatTween( |
|||
begin: peak, |
|||
end: end |
|||
).chain(new CurveTween(curve: secondCurve)) |
|||
), |
|||
} |
|||
).animate(parent); |
|||
} |
|||
|
|||
public static Animation<Offset> _getOffsetEmphasizedEasingAnimation( |
|||
Offset begin, |
|||
Offset peak, |
|||
Offset 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<Offset>( |
|||
new List<TweenSequenceItem<Offset>>{ |
|||
new TweenSequenceItem<Offset>( |
|||
weight: firstWeight, |
|||
tween: new OffsetTween( |
|||
begin: begin, |
|||
end: peak |
|||
).chain(new CurveTween(curve: firstCurve)) |
|||
), |
|||
new TweenSequenceItem<Offset>( |
|||
weight: secondWeight, |
|||
tween: new OffsetTween( |
|||
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(); |
|||
|
|||
public 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"); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
public class _ExpandingBottomSheetState : TickerProviderStateMixin<ExpandingBottomSheet> { |
|||
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: TimeSpan.FromMilliseconds(500), |
|||
vsync: this |
|||
); |
|||
} |
|||
|
|||
public override void dispose() { |
|||
_controller.dispose(); |
|||
base.dispose(); |
|||
} |
|||
|
|||
Animation<float> _getWidthAnimation(float screenWidth) { |
|||
if (_controller.status == AnimationStatus.forward) { |
|||
return new FloatTween(begin: _width, end: screenWidth).animate( |
|||
new CurvedAnimation( |
|||
parent: _controller.view, |
|||
curve: new Interval(0.0f, 0.3f, curve: Curves.fastOutSlowIn) |
|||
) |
|||
); |
|||
} else { |
|||
return expanding_buttom_sheetUtils._getFloatEmphasizedEasingAnimation( |
|||
begin: _width, |
|||
peak: expanding_buttom_sheetUtils._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 expanding_buttom_sheetUtils._getFloatEmphasizedEasingAnimation( |
|||
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 FloatTween( |
|||
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 FloatTween(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 expanding_buttom_sheetUtils._getFloatEmphasizedEasingAnimation( |
|||
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 FloatTween(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.
|
|||
public 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 Container( |
|||
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: TimeSpan.FromMilliseconds(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<AppStateModel>.of(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 new Container( |
|||
width: _widthAnimation.value, |
|||
height: _heightAnimation.value, |
|||
child: new Material( |
|||
animationDuration: TimeSpan.FromMilliseconds(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 = expanding_buttom_sheetUtils._getOffsetEmphasizedEasingAnimation( |
|||
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() { |
|||
if (!_isOpen) { |
|||
//SystemNavigator.pop();
|
|||
return Future.value(true).to<bool>(); |
|||
} |
|||
|
|||
close(); |
|||
return Future.value(true).to<bool>(); |
|||
} |
|||
|
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new AnimatedSize( |
|||
key: _expandingBottomSheetKey, |
|||
duration: TimeSpan.FromMilliseconds(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 = GlobalKey<AnimatedListState>.key(); |
|||
|
|||
_ListModel _list; |
|||
List<int> _internalList; |
|||
|
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
_list = new _ListModel( |
|||
listKey: _listKey, |
|||
initialItems: ScopedModel<AppStateModel>.of(context).productsInCart.Keys.ToList(), |
|||
removedItemBuilder: _buildRemovedThumbnail |
|||
); |
|||
_internalList = new List<int>(_list.list); |
|||
} |
|||
|
|||
Product _productWithId(int productId) { |
|||
AppStateModel model = ScopedModel<AppStateModel>.of(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 FloatTween(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.ElementAt(index))); |
|||
} |
|||
|
|||
void _updateLists() { |
|||
_internalList = ScopedModel<AppStateModel>.of(context).productsInCart.Keys.ToList(); |
|||
HashSet<int> internalSet = new HashSet<int>(_internalList); |
|||
HashSet<int> listSet = new HashSet<int>(_list.list); |
|||
|
|||
HashSet<int> difference = new HashSet<int>(); |
|||
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.ElementAt(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 FileImage( |
|||
product.assetName |
|||
), |
|||
fit: BoxFit.cover |
|||
), |
|||
borderRadius: BorderRadius.all(Radius.circular(10.0f)) |
|||
), |
|||
margin: EdgeInsets.only(left: 16.0f) |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
public delegate Widget RemovedItemBuilder(int item, BuildContext context, Animation<float> animation) ; |
|||
public class _ListModel { |
|||
public _ListModel( |
|||
GlobalKey<AnimatedListState> listKey, |
|||
RemovedItemBuilder removedItemBuilder, |
|||
IEnumerable<int> initialItems = null |
|||
) |
|||
{ |
|||
D.assert(listKey != null); |
|||
D.assert(removedItemBuilder != null); |
|||
_items = initialItems?.ToList() ?? new List<int>(); |
|||
this.listKey = listKey; |
|||
this.removedItemBuilder = removedItemBuilder; |
|||
|
|||
} |
|||
|
|||
public readonly GlobalKey<AnimatedListState> listKey; |
|||
|
|||
//public readonly Delegate removedItemBuilder;
|
|||
public RemovedItemBuilder removedItemBuilder; |
|||
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: TimeSpan.FromMilliseconds(225)); |
|||
} |
|||
|
|||
public void remove(int product) { |
|||
int index = _items.IndexOf(product); |
|||
if (index >= 0) { |
|||
_removeAt(index); |
|||
} |
|||
} |
|||
|
|||
public void _removeAt(int index) |
|||
{ |
|||
int removedItem = _items.ElementAt(index); |
|||
_items.RemoveAt(index); |
|||
_animatedList.removeItem(index, (BuildContext context, Animation<float> animation) =>{ |
|||
return removedItemBuilder(removedItem, context, animation); |
|||
}); |
|||
|
|||
} |
|||
|
|||
public int length |
|||
{ |
|||
get |
|||
{ |
|||
return _items.Count; |
|||
} |
|||
} |
|||
|
|||
public int ElementAt(int index) => _items[index]; |
|||
|
|||
int indexOf(int item) => _items.IndexOf(item); |
|||
|
|||
public List<int> list |
|||
{ |
|||
get |
|||
{ |
|||
return _items; |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
using System.Collections.Generic; |
|||
using UIWidgetsGallery.demo.shrine.model; |
|||
using UIWidgetsGallery.demo.shrine.supplemental; |
|||
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 context1, Widget child, AppStateModel model) => { |
|||
return new 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), |
|||
} |
|||
); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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<object>(); |
|||
} |
|||
) |
|||
), |
|||
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.file("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<object>(); |
|||
} |
|||
), |
|||
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) |
|||
); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UIWidgetsGallery.demo.shrine.model; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.material; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using Image = Unity.UIWidgets.widgets.Image; |
|||
using TextStyle = Unity.UIWidgets.painting.TextStyle; |
|||
|
|||
namespace UIWidgetsGallery.demo.shrine |
|||
{ |
|||
|
|||
public class shopping_cartUtils |
|||
{ |
|||
public static readonly float _leftColumnWidth = 60.0f; |
|||
} |
|||
|
|||
|
|||
public class ShoppingCartPage : StatefulWidget { |
|||
|
|||
public override State createState() => new _ShoppingCartPageState(); |
|||
} |
|||
|
|||
public class _ShoppingCartPageState : State<ShoppingCartPage> { |
|||
List<Widget> _createShoppingCartRows(AppStateModel model) |
|||
{ |
|||
List<Widget> widgets = null; |
|||
for (int id = 0; id < model.productsInCart.Count; id++) |
|||
{ |
|||
widgets.Add(new ShoppingCartRow( |
|||
product: model.getProductById(id), |
|||
quantity: model.productsInCart[id], |
|||
onPressed: ()=> { |
|||
model.removeItemFromCart(id); |
|||
} |
|||
)); |
|||
} |
|||
|
|||
return widgets; |
|||
} |
|||
|
|||
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; |
|||
|
|||
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( |
|||
$"${model.totalCost:F}", |
|||
style: largeAmountStyle |
|||
) |
|||
} |
|||
), |
|||
new SizedBox(height: 16.0f), |
|||
new Row( |
|||
children: new List<Widget> |
|||
{ |
|||
new Expanded( |
|||
child: new Text("Subtotal:") |
|||
), |
|||
new Text( |
|||
$" $ {model.subtotalCost:F}", |
|||
style: smallAmountStyle |
|||
) |
|||
} |
|||
), |
|||
new SizedBox(height: 4.0f), |
|||
new Row( |
|||
children: new List<Widget> |
|||
{ |
|||
new Expanded( |
|||
child: new Text("Shipping:") |
|||
), |
|||
new Text( |
|||
$" $ {model.shippingCost:F}", |
|||
style: smallAmountStyle |
|||
) |
|||
} |
|||
), |
|||
new SizedBox(height: 4.0f), |
|||
new Row( |
|||
children: new List<Widget> |
|||
{ |
|||
|
|||
new Expanded( |
|||
child: new Text("Tax:") |
|||
), |
|||
new Text( |
|||
$"${model.tax:F}", |
|||
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) { |
|||
|
|||
ThemeData localTheme = Theme.of(context); |
|||
|
|||
return new Padding( |
|||
padding: EdgeInsets.only(bottom: 16.0f), |
|||
child: new Row( |
|||
key: new 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 $ {product.price : D} ") |
|||
} |
|||
), |
|||
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 |
|||
) |
|||
} |
|||
) |
|||
) |
|||
) |
|||
} |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
using System; |
|||
using UIWidgetsGallery.demo.shrine; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.widgets; |
|||
|
|||
namespace UIWidgetsGallery.demo |
|||
{ |
|||
class ShrineDemo : StatelessWidget { |
|||
public ShrineDemo(Key key = null) : base(key: key){} |
|||
|
|||
public static readonly string routeName = "/shrine"; // Used by the Gallery app.
|
|||
|
|||
public override Widget build(BuildContext context) => new ShrineApp(); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b5da65292c17429ba697ebdefb05c8e4 |
|||
timeCreated: 1612512022 |
部分文件因为文件数量过多而无法显示
撰写
预览
正在加载...
取消
保存
Reference in new issue