blob: 272d6db818d521705015c57411838613742ec361 [file] [log] [blame]
// Copyright 2019 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/gallery_localizations.dart';
import 'package:gallery/layout/adaptive.dart';
import 'package:gallery/layout/image_placeholder.dart';
import 'package:gallery/studies/shrine/model/app_state_model.dart';
import 'package:gallery/studies/shrine/model/product.dart';
import 'package:intl/intl.dart';
import 'package:scoped_model/scoped_model.dart';
class MobileProductCard extends StatelessWidget {
const MobileProductCard({
super.key,
this.imageAspectRatio = 33 / 49,
required this.product,
}) : assert(imageAspectRatio > 0);
final double imageAspectRatio;
final Product product;
static const double defaultTextBoxHeight = 65;
@override
Widget build(BuildContext context) {
return Semantics(
container: true,
button: true,
enabled: true,
child: _buildProductCard(
context: context,
product: product,
imageAspectRatio: imageAspectRatio,
),
);
}
}
class DesktopProductCard extends StatelessWidget {
const DesktopProductCard({
super.key,
required this.product,
required this.imageWidth,
});
final Product product;
final double imageWidth;
@override
Widget build(BuildContext context) {
return _buildProductCard(
context: context,
product: product,
imageWidth: imageWidth,
);
}
}
Widget _buildProductCard({
required BuildContext context,
required Product product,
double? imageWidth,
double? imageAspectRatio,
}) {
final isDesktop = isDisplayDesktop(context);
// In case of desktop , imageWidth is passed through [DesktopProductCard] in
// case of mobile imageAspectRatio is passed through [MobileProductCard].
// Below assert is so that correct combination should always be present.
assert(isDesktop && imageWidth != null ||
!isDesktop && imageAspectRatio != null);
final formatter = NumberFormat.simpleCurrency(
decimalDigits: 0,
locale: Localizations.localeOf(context).toString(),
);
final theme = Theme.of(context);
final imageWidget = FadeInImagePlaceholder(
image: AssetImage(product.assetName, package: product.assetPackage),
placeholder: Container(
color: Colors.black.withOpacity(0.1),
width: imageWidth,
height: imageWidth == null ? null : imageWidth / product.assetAspectRatio,
),
fit: BoxFit.cover,
width: isDesktop ? imageWidth : null,
height: isDesktop ? null : double.infinity,
excludeFromSemantics: true,
);
return ScopedModelDescendant<AppStateModel>(
builder: (context, child, model) {
return Semantics(
hint: GalleryLocalizations.of(context)!
.shrineScreenReaderProductAddToCart,
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
model.addProductToCart(product.id);
},
child: child,
),
),
);
},
child: Stack(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
isDesktop
? imageWidget
: AspectRatio(
aspectRatio: imageAspectRatio!,
child: imageWidget,
),
SizedBox(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 23),
SizedBox(
width: imageWidth,
child: Text(
product.name(context),
style: theme.textTheme.labelLarge,
softWrap: true,
textAlign: TextAlign.center,
),
),
const SizedBox(height: 4),
Text(
formatter.format(product.price),
style: theme.textTheme.bodySmall,
),
],
),
),
],
),
const Padding(
padding: EdgeInsets.all(16),
child: Icon(Icons.add_shopping_cart),
),
],
),
);
}