You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
samples/material_studies/shrine/lib/shopping_cart.dart

273 lines
8.8 KiB

// Copyright 2018-present the Flutter authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:scoped_model/scoped_model.dart';
import 'colors.dart';
import 'expanding_bottom_sheet.dart';
import 'model/app_state_model.dart';
import 'model/product.dart';
const _leftColumnWidth = 60.0;
class ShoppingCartPage extends StatefulWidget {
@override
_ShoppingCartPageState createState() => _ShoppingCartPageState();
}
class _ShoppingCartPageState extends State<ShoppingCartPage> {
List<Widget> _createShoppingCartRows(AppStateModel model) {
return model.productsInCart.keys
.map(
(id) => ShoppingCartRow(
product: model.getProductById(id),
quantity: model.productsInCart[id],
onPressed: () {
model.removeItemFromCart(id);
},
),
)
.toList();
}
@override
Widget build(BuildContext context) {
final localTheme = Theme.of(context);
return Scaffold(
backgroundColor: kShrinePink50,
body: SafeArea(
child: Container(
child: ScopedModelDescendant<AppStateModel>(
builder: (context, child, model) {
return Stack(
children: [
ListView(
children: [
Row(
children: [
SizedBox(
width: _leftColumnWidth,
child: IconButton(
icon: const Icon(Icons.keyboard_arrow_down),
onPressed: () =>
ExpandingBottomSheet.of(context).close()),
),
Text(
'CART',
style: localTheme.textTheme.subhead
.copyWith(fontWeight: FontWeight.w600),
),
const SizedBox(width: 16.0),
Text('${model.totalCartQuantity} ITEMS'),
],
),
const SizedBox(height: 16.0),
Column(
children: _createShoppingCartRows(model),
),
ShoppingCartSummary(model: model),
const SizedBox(height: 100.0),
],
),
Positioned(
bottom: 16.0,
left: 16.0,
right: 16.0,
child: RaisedButton(
shape: const BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0)),
),
color: kShrinePink100,
splashColor: kShrineBrown600,
child: const Padding(
padding: EdgeInsets.symmetric(vertical: 12.0),
child: Text('CLEAR CART'),
),
onPressed: () {
model.clearCart();
ExpandingBottomSheet.of(context).close();
},
),
),
],
);
},
),
),
),
);
}
}
class ShoppingCartSummary extends StatelessWidget {
ShoppingCartSummary({this.model});
final AppStateModel model;
@override
Widget build(BuildContext context) {
final smallAmountStyle =
Theme.of(context).textTheme.body1.copyWith(color: kShrineBrown600);
final largeAmountStyle = Theme.of(context).textTheme.display1;
final formatter = NumberFormat.simpleCurrency(
decimalDigits: 2, locale: Localizations.localeOf(context).toString());
return Row(
children: [
SizedBox(width: _leftColumnWidth),
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Expanded(
child: Text('TOTAL'),
),
Text(
formatter.format(model.totalCost),
style: largeAmountStyle,
),
],
),
const SizedBox(height: 16.0),
Row(
children: [
const Expanded(
child: Text('Subtotal:'),
),
Text(
formatter.format(model.subtotalCost),
style: smallAmountStyle,
),
],
),
const SizedBox(height: 4.0),
Row(
children: [
const Expanded(
child: Text('Shipping:'),
),
Text(
formatter.format(model.shippingCost),
style: smallAmountStyle,
),
],
),
const SizedBox(height: 4.0),
Row(
children: [
const Expanded(
child: Text('Tax:'),
),
Text(
formatter.format(model.tax),
style: smallAmountStyle,
),
],
),
],
),
),
),
],
);
}
}
class ShoppingCartRow extends StatelessWidget {
ShoppingCartRow(
{@required this.product, @required this.quantity, this.onPressed});
final Product product;
final int quantity;
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
final formatter = NumberFormat.simpleCurrency(
decimalDigits: 0, locale: Localizations.localeOf(context).toString());
final localTheme = Theme.of(context);
return Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: Row(
key: ValueKey(product.id),
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: _leftColumnWidth,
child: IconButton(
icon: const Icon(Icons.remove_circle_outline),
onPressed: onPressed,
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(
product.assetName,
package: product.assetPackage,
fit: BoxFit.cover,
width: 75.0,
height: 75.0,
),
const SizedBox(width: 16.0),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text('Quantity: $quantity'),
),
Text('x ${formatter.format(product.price)}'),
],
),
Text(
product.name,
style: localTheme.textTheme.subhead
.copyWith(fontWeight: FontWeight.w600),
),
],
),
),
],
),
const SizedBox(height: 16.0),
const Divider(
color: kShrineBrown900,
height: 10.0,
),
],
),
),
),
],
),
);
}
}