asymmetric_view.dart 2.81 KB
Newer Older
1 2 3
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

import 'package:flutter/material.dart';

import 'package:flutter_gallery/demo/shrine/model/product.dart';
import 'package:flutter_gallery/demo/shrine/supplemental/product_columns.dart';

class AsymmetricView extends StatelessWidget {
  const AsymmetricView({Key key, this.products}) : super(key: key);

  final List<Product> products;

  List<Container> _buildColumns(BuildContext context) {
    if (products == null || products.isEmpty) {
      return const <Container>[];
    }

    // 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), (int index) {
      double width = .59 * 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,
        );
        width += 32.0;
      } else {
        /// Odd cases
        column = OneProductCardColumn(
          product: products[_oddCasesIndex(index)],
        );
      }
      return Container(
        width: width,
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16.0),
          child: column,
        ),
      );
    }).toList();
  }

  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 ListView(
      scrollDirection: Axis.horizontal,
      padding: const EdgeInsets.fromLTRB(0.0, 34.0, 16.0, 44.0),
      children: _buildColumns(context),
      physics: const AlwaysScrollableScrollPhysics(),
    );
  }
}