Commit 9192f672 authored by Hans Muller's avatar Hans Muller Committed by GitHub

Shrine gallery demo: support for landscape layout (#9025)

parent 23981f59
...@@ -47,8 +47,8 @@ int _columnSpanAtIndex(int index) { ...@@ -47,8 +47,8 @@ int _columnSpanAtIndex(int index) {
// The Shrine home page arranges the product cards into two columns. The card // The Shrine home page arranges the product cards into two columns. The card
// on every 4th and 5th row spans two columns. // on every 4th and 5th row spans two columns.
class ShrineGridLayout extends SliverGridLayout { class _ShrineGridLayout extends SliverGridLayout {
const ShrineGridLayout({ const _ShrineGridLayout({
@required this.rowStride, @required this.rowStride,
@required this.columnStride, @required this.columnStride,
@required this.tileHeight, @required this.tileHeight,
...@@ -95,14 +95,14 @@ class ShrineGridLayout extends SliverGridLayout { ...@@ -95,14 +95,14 @@ class ShrineGridLayout extends SliverGridLayout {
} }
} }
class ShrineGridDelegate extends SliverGridDelegate { class _ShrineGridDelegate extends SliverGridDelegate {
static const double _kSpacing = 8.0; static const double _kSpacing = 8.0;
@override @override
SliverGridLayout getLayout(SliverConstraints constraints) { SliverGridLayout getLayout(SliverConstraints constraints) {
final double tileWidth = (constraints.crossAxisExtent - _kSpacing) / 2.0; final double tileWidth = (constraints.crossAxisExtent - _kSpacing) / 2.0;
final double tileHeight = 40.0 + 144.0 + 40.0; final double tileHeight = 40.0 + 144.0 + 40.0;
return new ShrineGridLayout( return new _ShrineGridLayout(
tileWidth: tileWidth, tileWidth: tileWidth,
tileHeight: tileHeight, tileHeight: tileHeight,
rowStride: tileHeight + _kSpacing, rowStride: tileHeight + _kSpacing,
...@@ -114,9 +114,9 @@ class ShrineGridDelegate extends SliverGridDelegate { ...@@ -114,9 +114,9 @@ class ShrineGridDelegate extends SliverGridDelegate {
bool shouldRelayout(covariant SliverGridDelegate oldDelegate) => false; bool shouldRelayout(covariant SliverGridDelegate oldDelegate) => false;
} }
/// Displays the Vendor's name and avatar. // Displays the Vendor's name and avatar.
class VendorItem extends StatelessWidget { class _VendorItem extends StatelessWidget {
VendorItem({ Key key, this.vendor }) : super(key: key) { _VendorItem({ Key key, this.vendor }) : super(key: key) {
assert(vendor != null); assert(vendor != null);
} }
...@@ -145,10 +145,10 @@ class VendorItem extends StatelessWidget { ...@@ -145,10 +145,10 @@ class VendorItem extends StatelessWidget {
} }
} }
/// Displays the product's price. If the product is in the shopping cart the background // Displays the product's price. If the product is in the shopping cart then the
/// is highlighted. // background is highlighted.
abstract class PriceItem extends StatelessWidget { abstract class _PriceItem extends StatelessWidget {
PriceItem({ Key key, this.product }) : super(key: key) { _PriceItem({ Key key, this.product }) : super(key: key) {
assert(product != null); assert(product != null);
} }
...@@ -167,8 +167,8 @@ abstract class PriceItem extends StatelessWidget { ...@@ -167,8 +167,8 @@ abstract class PriceItem extends StatelessWidget {
} }
} }
class ProductPriceItem extends PriceItem { class _ProductPriceItem extends _PriceItem {
ProductPriceItem({ Key key, Product product }) : super(key: key, product: product); _ProductPriceItem({ Key key, Product product }) : super(key: key, product: product);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -180,8 +180,8 @@ class ProductPriceItem extends PriceItem { ...@@ -180,8 +180,8 @@ class ProductPriceItem extends PriceItem {
} }
} }
class FeaturePriceItem extends PriceItem { class _FeaturePriceItem extends _PriceItem {
FeaturePriceItem({ Key key, Product product }) : super(key: key, product: product); _FeaturePriceItem({ Key key, Product product }) : super(key: key, product: product);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -193,34 +193,54 @@ class FeaturePriceItem extends PriceItem { ...@@ -193,34 +193,54 @@ class FeaturePriceItem extends PriceItem {
} }
} }
/// Layout the main left and right elements of a FeatureItem. class _HeadingLayout extends MultiChildLayoutDelegate {
class FeatureLayout extends MultiChildLayoutDelegate { _HeadingLayout();
FeatureLayout();
static final String left = 'left'; static final String price = 'price';
static final String right = 'right'; static final String image = 'image';
static final String title = 'title';
static final String description = 'description';
static final String vendor = 'vendor';
// Horizontally: the feature product image appears on the left and
// occupies 50% of the available width; the feature product's
// description apepars on the right and occupies 50% of the available
// width + unitSize. The left and right widgets overlap and the right
// widget is stacked on top.
@override @override
void performLayout(Size size) { void performLayout(Size size) {
final Size priceSize = layoutChild(price, new BoxConstraints.loose(size));
positionChild(price, new Offset(size.width - priceSize.width, 0.0));
final double halfWidth = size.width / 2.0; final double halfWidth = size.width / 2.0;
layoutChild(left, new BoxConstraints.tightFor(width: halfWidth, height: size.height)); final double halfHeight = size.height / 2.0;
positionChild(left, Offset.zero); final double halfUnit = unitSize / 2.0;
layoutChild(right, new BoxConstraints.expand(width: halfWidth + unitSize, height: size.height)); const double margin = 16.0;
positionChild(right, new Offset(halfWidth - unitSize, 0.0));
final Size imageSize = layoutChild(image, new BoxConstraints.loose(size));
final double imageX = imageSize.width < halfWidth - halfUnit
? halfWidth / 2.0 - imageSize.width / 2.0 - halfUnit
: halfWidth - imageSize.width;
positionChild(image, new Offset(imageX, halfHeight - imageSize.height / 2.0));
final double maxTitleWidth = halfWidth + unitSize - margin;
final BoxConstraints titleBoxConstraints = new BoxConstraints(maxWidth: maxTitleWidth);
final Size titleSize = layoutChild(title, titleBoxConstraints);
final double titleX = halfWidth - unitSize;
final double titleY = halfHeight - titleSize.height;
positionChild(title, new Offset(titleX, titleY));
final Size descriptionSize = layoutChild(description, titleBoxConstraints);
final double descriptionY = titleY + titleSize.height + margin;
positionChild(description, new Offset(titleX, descriptionY));
layoutChild(vendor, titleBoxConstraints);
final double vendorY = descriptionY + descriptionSize.height + margin;
positionChild(vendor, new Offset(titleX, vendorY));
} }
@override @override
bool shouldRelayout(FeatureLayout oldDelegate) => false; bool shouldRelayout(_HeadingLayout oldDelegate) => false;
} }
/// A card that highlights the "featured" catalog item. // A card that highlights the "featured" catalog item.
class FeatureItem extends StatelessWidget { class _Heading extends StatelessWidget {
FeatureItem({ Key key, this.product }) : super(key: key) { _Heading({ Key key, this.product }) : super(key: key) {
assert(product.featureTitle != null); assert(product.featureTitle != null);
assert(product.featureDescription != null); assert(product.featureDescription != null);
} }
...@@ -229,63 +249,39 @@ class FeatureItem extends StatelessWidget { ...@@ -229,63 +249,39 @@ class FeatureItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final Size screenSize = MediaQuery.of(context).size;
final ShrineTheme theme = ShrineTheme.of(context); final ShrineTheme theme = ShrineTheme.of(context);
return new AspectRatio( return new SizedBox(
aspectRatio: 3.0 / 3.5, height: screenSize.width > screenSize.height
? (screenSize.height - kToolbarHeight) * 0.85
: (screenSize.height - kToolbarHeight) * 0.70,
child: new Container( child: new Container(
decoration: new BoxDecoration( decoration: new BoxDecoration(
backgroundColor: theme.cardBackgroundColor, backgroundColor: theme.cardBackgroundColor,
border: new Border(bottom: new BorderSide(color: theme.dividerColor)), border: new Border(bottom: new BorderSide(color: theme.dividerColor)),
), ),
child: new Column( child: new CustomMultiChildLayout(
crossAxisAlignment: CrossAxisAlignment.stretch, delegate: new _HeadingLayout(),
children: <Widget>[ children: <Widget>[
new SizedBox( new LayoutId(
height: unitSize, id: _HeadingLayout.price,
child: new Align( child: new _FeaturePriceItem(product: product),
alignment: FractionalOffset.topRight,
child: new FeaturePriceItem(product: product),
),
), ),
new Expanded( new LayoutId(
child: new CustomMultiChildLayout( id: _HeadingLayout.image,
delegate: new FeatureLayout(), child: new Image.asset(product.imageAsset, fit: BoxFit.cover),
children: <Widget>[ ),
new LayoutId( new LayoutId(
id: FeatureLayout.left, id: _HeadingLayout.title,
child: new ClipRect( child: new Text(product.featureTitle, style: theme.featureTitleStyle),
child: new OverflowBox( ),
minWidth: 340.0, new LayoutId(
maxWidth: 340.0, id: _HeadingLayout.description,
minHeight: 340.0, child: new Text(product.featureDescription, style: theme.featureStyle),
maxHeight: 340.0, ),
alignment: FractionalOffset.topRight, new LayoutId(
child: new Image.asset(product.imageAsset, fit: BoxFit.cover), id: _HeadingLayout.vendor,
), child: new _VendorItem(vendor: product.vendor),
),
),
new LayoutId(
id: FeatureLayout.right,
child: new Padding(
padding: const EdgeInsets.only(right: 16.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Padding(
padding: const EdgeInsets.only(top: 18.0),
child: new Text(product.featureTitle, style: theme.featureTitleStyle),
),
new Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: new Text(product.featureDescription, style: theme.featureStyle),
),
new VendorItem(vendor: product.vendor),
],
),
),
),
],
),
), ),
], ],
), ),
...@@ -294,9 +290,10 @@ class FeatureItem extends StatelessWidget { ...@@ -294,9 +290,10 @@ class FeatureItem extends StatelessWidget {
} }
} }
/// A card that displays a product's image, price, and vendor. // A card that displays a product's image, price, and vendor. The _ProductItem
class ProductItem extends StatelessWidget { // cards appear in a grid below the heading.
ProductItem({ Key key, this.product, this.onPressed }) : super(key: key) { class _ProductItem extends StatelessWidget {
_ProductItem({ Key key, this.product, this.onPressed }) : super(key: key) {
assert(product != null); assert(product != null);
} }
...@@ -312,7 +309,7 @@ class ProductItem extends StatelessWidget { ...@@ -312,7 +309,7 @@ class ProductItem extends StatelessWidget {
children: <Widget>[ children: <Widget>[
new Align( new Align(
alignment: FractionalOffset.centerRight, alignment: FractionalOffset.centerRight,
child: new ProductPriceItem(product: product), child: new _ProductPriceItem(product: product),
), ),
new Container( new Container(
width: 144.0, width: 144.0,
...@@ -325,7 +322,7 @@ class ProductItem extends StatelessWidget { ...@@ -325,7 +322,7 @@ class ProductItem extends StatelessWidget {
), ),
new Padding( new Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0), padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: new VendorItem(vendor: product.vendor), child: new _VendorItem(vendor: product.vendor),
), ),
], ],
), ),
...@@ -339,18 +336,18 @@ class ProductItem extends StatelessWidget { ...@@ -339,18 +336,18 @@ class ProductItem extends StatelessWidget {
} }
} }
/// The Shrine app's home page. Displays the featured item above all of the // The Shrine app's home page. Displays the featured item above a grid
/// product items arranged in two columns. // of the product items.
class ShrineHome extends StatefulWidget { class ShrineHome extends StatefulWidget {
@override @override
_ShrineHomeState createState() => new _ShrineHomeState(); _ShrineHomeState createState() => new _ShrineHomeState();
} }
class _ShrineHomeState extends State<ShrineHome> { class _ShrineHomeState extends State<ShrineHome> {
static final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>(debugLabel: 'Shrine Home'); static final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(debugLabel: 'Shrine Home');
static final ShrineGridDelegate gridDelegate = new ShrineGridDelegate(); static final _ShrineGridDelegate gridDelegate = new _ShrineGridDelegate();
Future<Null> showOrderPage(Product product) async { Future<Null> _showOrderPage(Product product) async {
final Order order = _shoppingCart[product] ?? new Order(product: product); final Order order = _shoppingCart[product] ?? new Order(product: product);
final Order completedOrder = await Navigator.push(context, new ShrineOrderRoute( final Order completedOrder = await Navigator.push(context, new ShrineOrderRoute(
order: order, order: order,
...@@ -371,13 +368,13 @@ class _ShrineHomeState extends State<ShrineHome> { ...@@ -371,13 +368,13 @@ class _ShrineHomeState extends State<ShrineHome> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final Product featured = _products.firstWhere((Product product) => product.featureDescription != null); final Product featured = _products.firstWhere((Product product) => product.featureDescription != null);
return new ShrinePage( return new ShrinePage(
scaffoldKey: scaffoldKey, scaffoldKey: _scaffoldKey,
products: _products, products: _products,
shoppingCart: _shoppingCart, shoppingCart: _shoppingCart,
body: new CustomScrollView( body: new CustomScrollView(
slivers: <Widget>[ slivers: <Widget>[
new SliverToBoxAdapter( new SliverToBoxAdapter(
child: new FeatureItem(product: featured), child: new _Heading(product: featured),
), ),
new SliverPadding( new SliverPadding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
...@@ -385,9 +382,9 @@ class _ShrineHomeState extends State<ShrineHome> { ...@@ -385,9 +382,9 @@ class _ShrineHomeState extends State<ShrineHome> {
gridDelegate: gridDelegate, gridDelegate: gridDelegate,
delegate: new SliverChildListDelegate( delegate: new SliverChildListDelegate(
_products.map((Product product) { _products.map((Product product) {
return new ProductItem( return new _ProductItem(
product: product, product: product,
onPressed: () { showOrderPage(product); }, onPressed: () { _showOrderPage(product); },
); );
}).toList(), }).toList(),
), ),
......
...@@ -9,10 +9,133 @@ import 'shrine_page.dart'; ...@@ -9,10 +9,133 @@ import 'shrine_page.dart';
import 'shrine_theme.dart'; import 'shrine_theme.dart';
import 'shrine_types.dart'; import 'shrine_types.dart';
/// Describes a product and vendor in detail, supports specifying // Displays the product title's, description, and order quantity dropdown.
/// a order quantity (0-5). Appears at the top of the OrderPage. class _ProductItem extends StatelessWidget {
class OrderItem extends StatelessWidget { _ProductItem({ Key key, this.product, this.quantity, this.onChanged }) : super(key: key) {
OrderItem({ Key key, this.product, this.quantity, this.quantityChanged }) : super(key: key) { assert(product != null);
assert(quantity != null);
assert(onChanged != null);
}
final Product product;
final int quantity;
final ValueChanged<int> onChanged;
@override
Widget build(BuildContext context) {
final ShrineTheme theme = ShrineTheme.of(context);
return new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Text(product.name, style: theme.featureTitleStyle),
const SizedBox(height: 24.0),
new Text(product.description, style: theme.featureStyle),
const SizedBox(height: 16.0),
new Padding(
padding: const EdgeInsets.only(top: 8.0, bottom: 8.0, right: 88.0),
child: new DropdownButtonHideUnderline(
child: new Container(
decoration: new BoxDecoration(
border: new Border.all(
color: const Color(0xFFD9D9D9),
),
),
child: new DropdownButton<int>(
items: <int>[0, 1, 2, 3, 4, 5].map((int value) {
return new DropdownMenuItem<int>(
value: value,
child: new Padding(
padding: const EdgeInsets.only(left: 8.0),
child: new Text('Quantity $value', style: theme.quantityMenuStyle),
),
);
}).toList(),
value: quantity,
onChanged: onChanged,
),
),
),
),
],
);
}
}
// Vendor name and description
class _VendorItem extends StatelessWidget {
_VendorItem({ Key key, this.vendor }) : super(key: key) {
assert(vendor != null);
}
final Vendor vendor;
@override
Widget build(BuildContext context) {
final ShrineTheme theme = ShrineTheme.of(context);
return new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new SizedBox(
height: 24.0,
child: new Align(
alignment: FractionalOffset.bottomLeft,
child: new Text(vendor.name, style: theme.vendorTitleStyle),
),
),
const SizedBox(height: 16.0),
new Text(vendor.description, style: theme.vendorStyle),
],
);
}
}
// Layout the order page's heading: the product's image, the
// title/description/dropdown product item, and the vendor item.
class _HeadingLayout extends MultiChildLayoutDelegate {
_HeadingLayout();
static final String image = 'image';
static final String icon = 'icon';
static final String product = 'product';
static final String vendor = 'vendor';
@override
void performLayout(Size size) {
const double margin = 56.0;
final bool landscape = size.width > size.height;
final double imageWidth = (landscape ? size.width / 2.0 : size.width) - margin * 2.0;
final BoxConstraints imageConstraints = new BoxConstraints(maxHeight: 224.0, maxWidth: imageWidth);
final Size imageSize = layoutChild(image, imageConstraints);
final double imageY = 0.0;
positionChild(image, new Offset(margin, imageY));
final double productWidth = landscape ? size.width / 2.0 : size.width - margin;
final BoxConstraints productConstraints = new BoxConstraints(maxWidth: productWidth);
final Size productSize = layoutChild(product, productConstraints);
final double productX = landscape ? size.width / 2.0 : margin;
final double productY = landscape ? 0.0 : imageY + imageSize.height + 16.0;
positionChild(product, new Offset(productX, productY));
final Size iconSize = layoutChild(icon, new BoxConstraints.loose(size));
positionChild(icon, new Offset(productX - iconSize.width - 16.0, productY + 8.0));
final double vendorWidth = landscape ? size.width - margin : productWidth;
layoutChild(vendor, new BoxConstraints(maxWidth: vendorWidth));
final double vendorX = landscape ? margin : productX;
final double vendorY = productY + productSize.height + 16.0;
positionChild(vendor, new Offset(vendorX, vendorY));
}
@override
bool shouldRelayout(_HeadingLayout oldDelegate) => true;
}
// Describes a product and vendor in detail, supports specifying
// a order quantity (0-5). Appears at the top of the OrderPage.
class _Heading extends StatelessWidget {
_Heading({ Key key, this.product, this.quantity, this.quantityChanged }) : super(key: key) {
assert(product != null); assert(product != null);
assert(quantity != null && quantity >= 0 && quantity <= 5); assert(quantity != null && quantity >= 0 && quantity <= 5);
} }
...@@ -23,92 +146,50 @@ class OrderItem extends StatelessWidget { ...@@ -23,92 +146,50 @@ class OrderItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ShrineTheme theme = ShrineTheme.of(context); final Size screenSize = MediaQuery.of(context).size;
return new Material( return new SizedBox(
type: MaterialType.card, height: (screenSize.height - kToolbarHeight) * 1.35,
elevation: 0, child: new Material(
child: new Padding( type: MaterialType.card,
padding: const EdgeInsets.only(left: 16.0, top: 18.0, right: 16.0, bottom: 24.0), elevation: 0,
child: new Column( child: new Padding(
crossAxisAlignment: CrossAxisAlignment.start, padding: const EdgeInsets.only(left: 16.0, top: 18.0, right: 16.0, bottom: 24.0),
children: <Widget>[ child: new CustomMultiChildLayout(
new Padding( delegate: new _HeadingLayout(),
padding: const EdgeInsets.only(left: 56.0), children: <Widget>[
child: new SizedBox( new LayoutId(
width: 248.0, id: _HeadingLayout.image,
height: 248.0,
child: new Hero( child: new Hero(
tag: product.tag, tag: product.tag,
child: new Image.asset(product.imageAsset, fit: BoxFit.contain), child: new Image.asset(
product.imageAsset,
fit: BoxFit.contain,
alignment: FractionalOffset.center,
),
), ),
), ),
), new LayoutId(
const SizedBox(height: 24.0), id: _HeadingLayout.icon,
new Row( child: new Icon(
children: <Widget>[ Icons.info_outline,
new Padding( size: 24.0,
padding: const EdgeInsets.symmetric(horizontal: 16.0), color: const Color(0xFFFFE0E0),
child: new Center(
child: new Icon(
Icons.info_outline,
size: 24.0,
color: const Color(0xFFFFE0E0),
),
),
), ),
new Expanded( ),
child: new Text(product.name, style: theme.featureTitleStyle), new LayoutId(
id: _HeadingLayout.product,
child: new _ProductItem(
product: product,
quantity: quantity,
onChanged: quantityChanged,
), ),
],
),
new Padding(
padding: const EdgeInsets.only(left: 56.0),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
const SizedBox(height: 24.0),
new Text(product.description, style: theme.featureStyle),
const SizedBox(height: 16.0),
new Padding(
padding: const EdgeInsets.only(top: 8.0, bottom: 8.0, right: 88.0),
child: new DropdownButtonHideUnderline(
child: new Container(
decoration: new BoxDecoration(
border: new Border.all(
color: const Color(0xFFD9D9D9),
),
),
child: new DropdownButton<int>(
items: <int>[0, 1, 2, 3, 4, 5].map((int value) {
return new DropdownMenuItem<int>(
value: value,
child: new Padding(
padding: const EdgeInsets.only(left: 8.0),
child: new Text('Quantity $value', style: theme.quantityMenuStyle),
),
);
}).toList(),
value: quantity,
onChanged: quantityChanged,
),
),
),
),
const SizedBox(height: 16.0),
new SizedBox(
height: 24.0,
child: new Align(
alignment: FractionalOffset.bottomLeft,
child: new Text(product.vendor.name, style: theme.vendorTitleStyle),
),
),
const SizedBox(height: 16.0),
new Text(product.vendor.description, style: theme.vendorStyle),
const SizedBox(height: 24.0),
],
), ),
), new LayoutId(
], id: _HeadingLayout.vendor,
child: new _VendorItem(vendor: product.vendor),
),
],
),
), ),
), ),
); );
...@@ -130,9 +211,9 @@ class OrderPage extends StatefulWidget { ...@@ -130,9 +211,9 @@ class OrderPage extends StatefulWidget {
_OrderPageState createState() => new _OrderPageState(); _OrderPageState createState() => new _OrderPageState();
} }
/// Displays a product's OrderItem above photos of all of the other products // Displays a product's heading above photos of all of the other products
/// arranged in two columns. Enables the user to specify a quantity and add an // arranged in two columns. Enables the user to specify a quantity and add an
/// order to the shopping cart. // order to the shopping cart.
class _OrderPageState extends State<OrderPage> { class _OrderPageState extends State<OrderPage> {
GlobalKey<ScaffoldState> scaffoldKey; GlobalKey<ScaffoldState> scaffoldKey;
...@@ -185,24 +266,20 @@ class _OrderPageState extends State<OrderPage> { ...@@ -185,24 +266,20 @@ class _OrderPageState extends State<OrderPage> {
), ),
body: new CustomScrollView( body: new CustomScrollView(
slivers: <Widget>[ slivers: <Widget>[
new SliverList( new SliverToBoxAdapter(
delegate: new SliverChildListDelegate(<Widget>[ child: new _Heading(
new OrderItem( product: config.order.product,
product: config.order.product, quantity: currentOrder.quantity,
quantity: currentOrder.quantity, quantityChanged: (int value) { updateOrder(quantity: value); },
quantityChanged: (int value) { updateOrder(quantity: value); }, ),
),
const SizedBox(height: 24.0),
]),
), ),
new SliverPadding( new SliverPadding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.fromLTRB(8.0, 32.0, 8.0, 8.0),
sliver: new SliverGrid( sliver: new SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
crossAxisCount: 2, maxCrossAxisExtent: 248.0,
mainAxisSpacing: 8.0, mainAxisSpacing: 8.0,
crossAxisSpacing: 8.0, crossAxisSpacing: 8.0,
childAspectRatio: 160.0 / 216.0, // width/height
), ),
delegate: new SliverChildListDelegate( delegate: new SliverChildListDelegate(
config.products config.products
...@@ -225,11 +302,11 @@ class _OrderPageState extends State<OrderPage> { ...@@ -225,11 +302,11 @@ class _OrderPageState extends State<OrderPage> {
} }
} }
/// Displays a full-screen modal OrderPage. // Displays a full-screen modal OrderPage.
/// //
/// The order field will be replaced each time the user reconfigures the order. // The order field will be replaced each time the user reconfigures the order.
/// When the user backs out of this route the completer's value will be the // When the user backs out of this route the completer's value will be the
/// final value of the order field. // final value of the order field.
class ShrineOrderRoute extends ShrinePageRoute<Order> { class ShrineOrderRoute extends ShrinePageRoute<Order> {
ShrineOrderRoute({ ShrineOrderRoute({
this.order, this.order,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment