dart - 如何在 flutter 中生成 'stacked card list view'?

标签 dart flutter flutter-layout

我想在 flutter 中构建类似于此链接的 ui。

https://github.com/loopeer/CardStackView/blob/master/screenshot/screenshot1.gif

enter image description here

主要的理想特征如下。

  • 类似于 ListView ,但卡片应堆叠在屏幕顶部。
  • 列表可以有无限项。所以应该回收旧卡以节省内存。
  • 我还想为每张卡片设置不同的尺寸。

首先,我找到了一些类似 ui 的“tinder”,并尝试了它们。 https://blog.geekyants.com/tinder-swipe-in-flutter-7e4fc56021bc

但是,用户需要每张卡片都刷一次,这需要用户刷很多次才能浏览列表项。

然后我可以以某种方式制作一个 ListView ,其项目与下一个项目重叠。

import 'package:flutter/material.dart';

class StackedList extends StatelessWidget {
  List<ItemCard> cards = [];

  StackedList() {
    for (int i = 0; i < 20; i++) {
      cards.add(ItemCard(i));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('title')),
      body: Container(
        child: ListView.builder(
          itemBuilder: (context, index) {
            return Align(
              alignment: Alignment.topCenter,
              heightFactor: 0.8,
              child: cards[index],
            );
          },
          itemCount: cards.length,
        ),
      ),
    );
  }
}

class ItemCard extends StatelessWidget {
  int index;

  ItemCard(this.index);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: const BoxDecoration(
        boxShadow: [
          BoxShadow(color: Colors.black, blurRadius: 20.0),
        ],
      ),
      child: SizedBox.fromSize(
        size: const Size(300, 400),
        child: Card(
          elevation: 5.0,
          color: index % 2 == 0 ? Colors.blue : Colors.red,
          child: Center(
            child: Text(index.toString()),
          ),
        ),
      ),
    );
  }
}

但是项目不会停在屏幕顶部,这不是我想要的。 我想我可以通过自定义 ScrollController 或 ScrollPhysics 来实现这种效果,但我不确定应该更改哪里。

最佳答案

您可以使用 SliverPersistentHeaderCustomScrollView 实现类似的行为,您可以将卡片包装在 GestureDetector 中以通过更改来修改它们的高度SliverPersistentHeaderDelegatemaxExtent 参数的值。这是我编写的一个小应用程序,它可以实现您正在寻找的东西:

import 'package:flutter/material.dart';
import 'dart:math' as math;

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Stacked list example',
      home: Scaffold(
          appBar: AppBar(
            title: Text("Stacked list example"),
            backgroundColor: Colors.black,
          ),
          body: StackedList()),
    );
  }
}

class StackedList extends StatelessWidget {
  final List<Color> _colors = Colors.primaries;
  static const _minHeight = 16.0;
  static const _maxHeight = 120.0;

  @override
  Widget build(BuildContext context) => CustomScrollView(
        slivers: _colors
            .map(
              (color) => StackedListChild(
                minHeight: _minHeight,
                maxHeight: _colors.indexOf(color) == _colors.length - 1
                    ? MediaQuery.of(context).size.height
                    : _maxHeight,
                pinned: true,
                child: Container(
                  color: _colors.indexOf(color) == 0
                      ? Colors.black
                      : _colors[_colors.indexOf(color) - 1],
                  child: Container(
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.vertical(
                          top: Radius.circular(_minHeight)),
                      color: color,
                    ),
                  ),
                ),
              ),
            )
            .toList(),
      );
}

class StackedListChild extends StatelessWidget {
  final double minHeight;
  final double maxHeight;
  final bool pinned;
  final bool floating;
  final Widget child;

  SliverPersistentHeaderDelegate get _delegate => _StackedListDelegate(
      minHeight: minHeight, maxHeight: maxHeight, child: child);

  const StackedListChild({
    Key key,
    @required this.minHeight,
    @required this.maxHeight,
    @required this.child,
    this.pinned = false,
    this.floating = false,
  })  : assert(child != null),
        assert(minHeight != null),
        assert(maxHeight != null),
        assert(pinned != null),
        assert(floating != null),
        super(key: key);

  @override
  Widget build(BuildContext context) => SliverPersistentHeader(
      key: key, pinned: pinned, floating: floating, delegate: _delegate);
}

class _StackedListDelegate extends SliverPersistentHeaderDelegate {
  final double minHeight;
  final double maxHeight;
  final Widget child;

  _StackedListDelegate({
    @required this.minHeight,
    @required this.maxHeight,
    @required this.child,
  });

  @override
  double get minExtent => minHeight;

  @override
  double get maxExtent => math.max(maxHeight, minHeight);

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return new SizedBox.expand(child: child);
  }

  @override
  bool shouldRebuild(_StackedListDelegate oldDelegate) {
    return maxHeight != oldDelegate.maxHeight ||
        minHeight != oldDelegate.minHeight ||
        child != oldDelegate.child;
  }
}

这是它的实际效果:

Stacked list example .gif

这里有一篇关于 Flutter 的条子的非常好的文章,可能在这方面对您有所帮助:

Slivers, demystified

希望这能帮助您找到正确的方向。

关于dart - 如何在 flutter 中生成 'stacked card list view'?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54494024/

相关文章:

flutter - 将FloatingActionButton居中

flutter - 如何 catch 文本字段小部件上的打字速度?

list - 将列表中的文本设置为 Text Widget flutter

flutter - 如何使 onTap 只响应实际文本而不是整个 ListTile 宽度

flutter - 使容器小部件高度适合父行高度

flutter - ListView 在列表顶部时与元素重叠

redux - 如何在 redux flutter app state 中定义对象并访问属性?

dart-polymer:无法从事件处理程序设置属性

Flutter:Stack 忽略 Image 的对齐属性

flutter - 小部件与 UI 组件有何不同?