代码之家  ›  专栏  ›  技术社区  ›  Notbad

模拟谷歌play应用中的水平图像列表

  •  1
  • Notbad  · 技术社区  · 5 年前

    我想创建一个小部件,其行为方式与google play应用程序中的水平滚动列表(例如推荐应用程序)相同。

    我的意思的一个例子:

    enter image description here

    我曾尝试使用页面查看器小部件,但它并没有完全实现同样的功能。一些值得注意的事实:

    1) 3张完美的正方形图像被完全看到,另一张部分显示。第二个(绿色的)就在中间。

    2) 最左边的图片(蓝色的)与标题“推荐给你”完全一致

    3) 所有4个正方形图像都有圆角。

    4) 它支持项目捕捉

    这是我在PageView小部件中模仿这种行为的最好方式:

    enter image description here

    • 红色是包含页面视图的容器的背景色。
    • 绿色表示偶数项,蓝色表示奇数项
    • 每件物品内都添加了一张100x100的图片。

    以下是代码:

      return Container(
          width: double.infinity,
          color: Colors.red,
          height: 100,
          child:
              PageView(
                  pageSnapping: true,
                  controller:
                      PageController(initialPage: 1, viewportFraction: 0.315),
                  children: List<Widget>.generate(10, (index) {
                    return Container(
                      color: index%2 ==0 ? Colors.green : Colors.blue,
                      child:Image.network("http://via.placeholder.com/100x100",
                          fit: BoxFit.fitHeight)
                    );
                  })));
    

    宽度我的代码有两件事是行不通的:

    1) 我无法通过使用viewportFraction,让右侧显示部分可见的项目,使3个完全可见的项目和第二个完全居中,就像原始图像形式google play一样。

    2) 我无法将圆角应用于项目图像,因为正如您所看到的,容器不是方形的,但图像是方形的。因此,当我应用ClipRect时,它的应用并不正确。我曾尝试添加另一个容器,将其大小强制为100x100,并在该容器内的图像上应用ClipRRect,但当它位于页面视图内时,给定的宽度/高度似乎没有效果。它们似乎在内部由PageView控制。

    那么,有谁能在这个问题上提供帮助,以获得与google play水平滚动列表相同的行为?

    干杯

    0 回复  |  直到 5 年前
        1
  •  1
  •   Doc    5 年前
    class SO extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        double edge = 120.0;
        double padding = edge / 10.0;
        return Scaffold(
          appBar: AppBar(),
          body: Container(
            color: Colors.red,
            padding: const EdgeInsets.symmetric(vertical: 8),
            child: SingleChildScrollView(
              scrollDirection: Axis.horizontal,
              child: Row(
                children: [
                  for (int i = 0; i < 10; i++)
                    Column(
                      mainAxisSize: MainAxisSize.min,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        Padding(
                          padding: EdgeInsets.all(padding),
                          child: ClipRRect(
                            borderRadius: BorderRadius.all(Radius.circular(edge * .2)),
                            child: Container(
                              width: edge,
                              height: edge,
                              color: Colors.blue,
                              child: Image.network("http://via.placeholder.com/${edge.round()}x${edge.round()}", fit: BoxFit.fitHeight),
                            ),
                          ),
                        ),
                        Container(
                          width: edge + padding,
                          padding: EdgeInsets.only(left: padding),
                          child: Text(
                            'foo app bar baz app apk',
                            maxLines: 2,
                            overflow: TextOverflow.ellipsis,
                          ),
                        ),
                        Padding(
                          padding: EdgeInsets.only(left: padding),
                          child: Row(
                            mainAxisSize: MainAxisSize.min,
                            children: <Widget>[
                              Text('4.2'),
                              Icon(
                                Icons.star,
                                size: 16,
                              )
                            ],
                          ),
                        ),
                      ],
                    )
                ],
              ),
            ),
          ),
        );
      }
    }
    

    这给了

    screenshot

        2
  •  1
  •   Crazy Lazy Cat    5 年前

    你可以修改 PageScrollPhysics 创建捕捉效果。

    我就是这么做的,

    import 'package:flutter/material.dart';
    
    Future<void> main() async {
      runApp(
        MaterialApp(
          home: new Main(),
        ),
      );
    }
    
    class Main extends StatefulWidget {
      @override
      _MainState createState() => _MainState();
    }
    
    class _MainState extends State<Main> {
      static const _scrollPhysics =
          const ExtentScrollPhysics(itemExtent: 80, separatorSpacing: 10);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Demo"),
          ),
          body: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 20.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    Text(
                      "Recommended for you",
                      style: const TextStyle(fontWeight: FontWeight.bold),
                    ),
                    IconButton(
                      icon: Icon(Icons.arrow_forward),
                      onPressed: () {},
                    )
                  ],
                ),
                SizedBox.fromSize(
                  size: Size.fromHeight(130),
                  child: ListView.separated(
                    scrollDirection: Axis.horizontal,
                    itemCount: 30,
                    physics: _scrollPhysics,
                    separatorBuilder: (context, _) =>
                        SizedBox(width: _scrollPhysics.dividerSpacing),
                    itemBuilder: (context, index) {
                      return SizedBox(
                        width: _scrollPhysics.itemExtent, // set height for vertical
                        child: CardItem(),
                      );
                    },
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    class CardItem extends StatefulWidget {
      @override
      _CardItemState createState() => _CardItemState();
    }
    
    class _CardItemState extends State<CardItem> {
      @override
      Widget build(BuildContext context) {
        return Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            AspectRatio(
              aspectRatio: 1,
              child: Container(
                decoration: BoxDecoration(
                  color: Colors.blue,
                  borderRadius: BorderRadius.circular(20.0),
                ),
              ),
            ),
            const SizedBox(height: 8.0),
            Text(
              "Tile Name",
              overflow: TextOverflow.ellipsis,
              maxLines: 2,
              style: const TextStyle(fontSize: 12.0),
            ),
            Text(
              "1 MB",
              style: const TextStyle(
                fontSize: 12.0,
                color: Colors.black54,
              ),
            ),
          ],
        );
      }
    }
    
    class ExtentScrollPhysics extends ScrollPhysics {
      final double itemExtent;
      final double dividerSpacing;
    
      const ExtentScrollPhysics(
          {ScrollPhysics parent, this.itemExtent, double separatorSpacing})
          : assert(itemExtent != null && itemExtent > 0),
            dividerSpacing = separatorSpacing ?? 0,
            super(parent: parent);
    
      @override
      ExtentScrollPhysics applyTo(ScrollPhysics ancestor) {
        return ExtentScrollPhysics(
          parent: buildParent(ancestor),
          itemExtent: itemExtent,
          separatorSpacing: dividerSpacing,
        );
      }
    
      double _getItem(ScrollPosition position) {
        return position.pixels / (itemExtent + dividerSpacing);
      }
    
      double _getPixels(ScrollPosition position, double item) {
        return item * (itemExtent + dividerSpacing);
      }
    
      double _getTargetPixels(
          ScrollPosition position, Tolerance tolerance, double velocity) {
        double page = _getItem(position);
        if (velocity < -tolerance.velocity)
          page -= 0.5;
        else if (velocity > tolerance.velocity) page += 0.5;
        return _getPixels(position, page.roundToDouble());
      }
    
      @override
      Simulation createBallisticSimulation(
          ScrollMetrics position, double velocity) {
        // If we're out of range and not headed back in range, defer to the parent
        // ballistics, which should put us back in range at a page boundary.
        if ((velocity <= 0.0 && position.pixels <= position.minScrollExtent) ||
            (velocity >= 0.0 && position.pixels >= position.maxScrollExtent))
          return super.createBallisticSimulation(position, velocity);
    
        final Tolerance tolerance = this.tolerance;
        final double target = _getTargetPixels(position, tolerance, velocity);
        if (target != position.pixels)
          return ScrollSpringSimulation(spring, position.pixels, target, velocity,
              tolerance: tolerance);
        return null;
      }
    
      @override
      bool get allowImplicitScrolling => false;
    }
    
    推荐文章