mirror of
https://github.com/flutter/samples.git
synced 2026-07-16 22:12:42 +00:00
[Gallery] Fix directory structure (#312)
This commit is contained in:
295
gallery/lib/studies/shrine/supplemental/asymmetric_view.dart
Normal file
295
gallery/lib/studies/shrine/supplemental/asymmetric_view.dart
Normal file
@@ -0,0 +1,295 @@
|
||||
// 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 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:gallery/data/gallery_options.dart';
|
||||
import 'package:gallery/layout/text_scale.dart';
|
||||
import 'package:gallery/studies/shrine/category_menu_page.dart';
|
||||
import 'package:gallery/studies/shrine/model/product.dart';
|
||||
import 'package:gallery/studies/shrine/supplemental/balanced_layout.dart';
|
||||
import 'package:gallery/studies/shrine/page_status.dart';
|
||||
import 'package:gallery/studies/shrine/supplemental/desktop_product_columns.dart';
|
||||
import 'package:gallery/studies/shrine/supplemental/product_columns.dart';
|
||||
import 'package:gallery/studies/shrine/supplemental/product_card.dart';
|
||||
|
||||
const _topPadding = 34.0;
|
||||
const _bottomPadding = 44.0;
|
||||
|
||||
const _cardToScreenWidthRatio = 0.59;
|
||||
|
||||
class MobileAsymmetricView extends StatelessWidget {
|
||||
const MobileAsymmetricView({Key key, this.products}) : super(key: key);
|
||||
|
||||
final List<Product> products;
|
||||
|
||||
List<Container> _buildColumns(
|
||||
BuildContext context,
|
||||
BoxConstraints constraints,
|
||||
) {
|
||||
if (products == null || products.isEmpty) {
|
||||
return const [];
|
||||
}
|
||||
|
||||
// Decide whether the page size and text size allow 2-column products.
|
||||
|
||||
final double cardHeight = (constraints.biggest.height -
|
||||
_topPadding -
|
||||
_bottomPadding -
|
||||
TwoProductCardColumn.spacerHeight) /
|
||||
2;
|
||||
|
||||
final double imageWidth =
|
||||
_cardToScreenWidthRatio * constraints.biggest.width -
|
||||
TwoProductCardColumn.horizontalPadding;
|
||||
|
||||
final double imageHeight = cardHeight -
|
||||
MobileProductCard.defaultTextBoxHeight *
|
||||
GalleryOptions.of(context).textScaleFactor(context);
|
||||
|
||||
final bool shouldUseAlternatingLayout =
|
||||
imageHeight > 0 && imageWidth / imageHeight < 49 / 33;
|
||||
|
||||
if (shouldUseAlternatingLayout) {
|
||||
// Alternating layout: a layout of alternating 2-product
|
||||
// and 1-product columns.
|
||||
//
|
||||
// This will return a list of columns. It will oscillate between the two
|
||||
// kinds of columns. Even cases of the index (0, 2, 4, etc) will be
|
||||
// TwoProductCardColumn and the odd cases will be OneProductCardColumn.
|
||||
//
|
||||
// Each pair of columns will advance us 3 products forward (2 + 1). That's
|
||||
// some kinda awkward math so we use _evenCasesIndex and _oddCasesIndex as
|
||||
// helpers for creating the index of the product list that will correspond
|
||||
// to the index of the list of columns.
|
||||
|
||||
return List<Container>.generate(_listItemCount(products.length), (index) {
|
||||
double width =
|
||||
_cardToScreenWidthRatio * MediaQuery.of(context).size.width;
|
||||
Widget column;
|
||||
if (index % 2 == 0) {
|
||||
/// Even cases
|
||||
final int bottom = _evenCasesIndex(index);
|
||||
column = TwoProductCardColumn(
|
||||
bottom: products[bottom],
|
||||
top:
|
||||
products.length - 1 >= bottom + 1 ? products[bottom + 1] : null,
|
||||
imageAspectRatio: imageWidth / imageHeight,
|
||||
);
|
||||
width += 32;
|
||||
} else {
|
||||
/// Odd cases
|
||||
column = OneProductCardColumn(
|
||||
product: products[_oddCasesIndex(index)],
|
||||
reverse: true,
|
||||
);
|
||||
}
|
||||
return Container(
|
||||
width: width,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: column,
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
} else {
|
||||
// Alternating layout: a layout of 1-product columns.
|
||||
|
||||
return [
|
||||
for (final product in products)
|
||||
Container(
|
||||
width: _cardToScreenWidthRatio * MediaQuery.of(context).size.width,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: OneProductCardColumn(
|
||||
product: product,
|
||||
reverse: false,
|
||||
),
|
||||
),
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
int _evenCasesIndex(int input) {
|
||||
// The operator ~/ is a cool one. It's the truncating division operator. It
|
||||
// divides the number and if there's a remainder / decimal, it cuts it off.
|
||||
// This is like dividing and then casting the result to int. Also, it's
|
||||
// functionally equivalent to floor() in this case.
|
||||
return input ~/ 2 * 3;
|
||||
}
|
||||
|
||||
int _oddCasesIndex(int input) {
|
||||
assert(input > 0);
|
||||
return (input / 2).ceil() * 3 - 1;
|
||||
}
|
||||
|
||||
int _listItemCount(int totalItems) {
|
||||
return (totalItems % 3 == 0)
|
||||
? totalItems ~/ 3 * 2
|
||||
: (totalItems / 3).ceil() * 2 - 1;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: PageStatus.of(context).cartController,
|
||||
builder: (context, child) => AnimatedBuilder(
|
||||
animation: PageStatus.of(context).menuController,
|
||||
builder: (context, child) => ExcludeSemantics(
|
||||
excluding: !productPageIsVisible(context),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsetsDirectional.fromSTEB(
|
||||
0,
|
||||
_topPadding,
|
||||
16,
|
||||
_bottomPadding,
|
||||
),
|
||||
children: _buildColumns(context, constraints),
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DesktopAsymmetricView extends StatelessWidget {
|
||||
const DesktopAsymmetricView({Key key, this.products}) : super(key: key);
|
||||
|
||||
final List<Product> products;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Determine the scale factor for the desktop asymmetric view.
|
||||
|
||||
final double textScaleFactor =
|
||||
GalleryOptions.of(context).textScaleFactor(context);
|
||||
|
||||
// When text is larger, the images becomes wider, but at half the rate.
|
||||
final double imageScaleFactor = reducedTextScale(context);
|
||||
|
||||
// When text is larger, horizontal padding becomes smaller.
|
||||
final double paddingScaleFactor = textScaleFactor >= 1.5 ? 0.25 : 1;
|
||||
|
||||
// Calculate number of columns
|
||||
|
||||
final double sidebar = desktopCategoryMenuPageWidth(context: context);
|
||||
final double minimumBoundaryWidth = 84 * paddingScaleFactor;
|
||||
final double columnWidth = 186 * imageScaleFactor;
|
||||
final double columnGapWidth = 24 * imageScaleFactor;
|
||||
final double windowWidth = MediaQuery.of(context).size.width;
|
||||
|
||||
final int idealColumnCount = max(
|
||||
1,
|
||||
((windowWidth + columnGapWidth - 2 * minimumBoundaryWidth - sidebar) /
|
||||
(columnWidth + columnGapWidth))
|
||||
.floor(),
|
||||
);
|
||||
|
||||
// Limit column width to fit within window when there is only one column.
|
||||
final double actualColumnWidth = idealColumnCount == 1
|
||||
? min(
|
||||
columnWidth,
|
||||
windowWidth - sidebar - 2 * minimumBoundaryWidth,
|
||||
)
|
||||
: columnWidth;
|
||||
|
||||
final int columnCount = min(idealColumnCount, max(products.length, 1));
|
||||
|
||||
return AnimatedBuilder(
|
||||
animation: PageStatus.of(context).cartController,
|
||||
builder: (context, child) => ExcludeSemantics(
|
||||
excluding: !productPageIsVisible(context),
|
||||
child: DesktopColumns(
|
||||
columnCount: columnCount,
|
||||
products: products,
|
||||
largeImageWidth: actualColumnWidth,
|
||||
smallImageWidth: columnCount > 1
|
||||
? columnWidth - columnGapWidth
|
||||
: actualColumnWidth,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DesktopColumns extends StatelessWidget {
|
||||
DesktopColumns({
|
||||
@required this.columnCount,
|
||||
@required this.products,
|
||||
@required this.largeImageWidth,
|
||||
@required this.smallImageWidth,
|
||||
});
|
||||
|
||||
final int columnCount;
|
||||
final List<Product> products;
|
||||
final double largeImageWidth;
|
||||
final double smallImageWidth;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Widget _gap = Container(width: 24);
|
||||
|
||||
final List<List<Product>> productCardLists = balancedLayout(
|
||||
context: context,
|
||||
columnCount: columnCount,
|
||||
products: products,
|
||||
largeImageWidth: largeImageWidth,
|
||||
smallImageWidth: smallImageWidth,
|
||||
);
|
||||
|
||||
final List<DesktopProductCardColumn> productCardColumns =
|
||||
List<DesktopProductCardColumn>.generate(
|
||||
columnCount,
|
||||
(column) {
|
||||
final bool alignToEnd =
|
||||
(column % 2 == 1) || (column == columnCount - 1);
|
||||
final bool startLarge = (column % 2 == 1);
|
||||
final bool lowerStart = (column % 2 == 1);
|
||||
return DesktopProductCardColumn(
|
||||
alignToEnd: alignToEnd,
|
||||
startLarge: startLarge,
|
||||
lowerStart: lowerStart,
|
||||
products: productCardLists[column],
|
||||
largeImageWidth: largeImageWidth,
|
||||
smallImageWidth: smallImageWidth,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return ListView(
|
||||
scrollDirection: Axis.vertical,
|
||||
children: [
|
||||
Container(height: 60),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Spacer(),
|
||||
...List<Widget>.generate(
|
||||
2 * columnCount - 1,
|
||||
(generalizedColumnIndex) {
|
||||
if (generalizedColumnIndex % 2 == 0) {
|
||||
return productCardColumns[generalizedColumnIndex ~/ 2];
|
||||
} else {
|
||||
return _gap;
|
||||
}
|
||||
},
|
||||
),
|
||||
Spacer(),
|
||||
],
|
||||
),
|
||||
Container(height: 60),
|
||||
],
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user