在Flutter中创建图像轮播

42

我该如何创建一个像下面例子一样的容器轮播?

我查看了Pageview类,但它只显示一个容器并隐藏其他容器。但是我想要在左侧和右侧部分地看到容器。在Flutter中有什么方法可以实现这个功能吗?

注意:当前容器应始终保持在中心位置。

Example

编辑:Darky给出了一个非常好的例子,但是我对他给出的代码有一个问题:

在调用controller.page的位置抛出了以下ArgumentError错误: AnimatedBuilder(animation: PageController#fc5f0(one client, offset 0.0), dirty, state: _AnimatedState#1ea5c): Invalid argument (lowerLimit): not a number: null –

这个错误是因为lowerLimit参数不是数字null。有人知道我该如何解决吗?


value = value<0.0 ? 0.0:value; value = value>1.0 ? 1.0:value; - Anarchofascist
3个回答

66

实际上你想要的是 PageView

PageView 接受一个 PageController 作为参数。该控制器拥有一个 viewportFraction 属性(默认为1.0),它以百分比表示所显示页面的主尺寸。

这意味着,使用 viewportFraction 为 0.5,主页将居中显示。并且您将在左右两侧各看到半个页面(如果有的话)

例如:

example_gif

class Carroussel extends StatefulWidget {
  @override
  _CarrousselState createState() => new _CarrousselState();
}

class _CarrousselState extends State<Carroussel> {
  PageController controller;
  int currentpage = 0;

  @override
  initState() {
    super.initState();
    controller = PageController(
      initialPage: currentpage,
      keepPage: false,
      viewportFraction: 0.5,
    );
  }

  @override
  dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          child: PageView.builder(
              onPageChanged: (value) {
                setState(() {
                  currentpage = value;
                });
              },
              controller: controller,
              itemBuilder: (context, index) => builder(index)),
        ),
      ),
    );
  }

  builder(int index) {
    return AnimatedBuilder(
      animation: controller,
      builder: (context, child) {
        double value = 1.0;
        if (controller.position.haveDimensions) {
          value = controller.page - index;
          value = (1 - (value.abs() * .5)).clamp(0.0, 1.0);
        }

        return Center(
          child: SizedBox(
            height: Curves.easeOut.transform(value) * 300,
            width: Curves.easeOut.transform(value) * 250,
            child: child,
          ),
        );
      },
      child: Container(
        margin: const EdgeInsets.all(8.0),
        color: index % 2 == 0 ? Colors.blue : Colors.red,
      ),
    );
  }
}

4
构建AnimatedBuilder(animation: PageController#fc5f0(one客户端,偏移量0.0),dirty,state:_AnimatedState#1ea5c)时抛出了以下ArgumentError: 无效参数(lowerLimit):不是数字:null。 - Lukas Kirner
1
我不理解动画是如何工作的。我看到当PageView滚动时,构建器函数被调用。但这是如何实现的?感谢AnimatedBuilder小部件?还是控制器?:// - Gabbr Issimo
@calebisstupid:请告诉我们在哪里添加这些值...因为我已经卡了几个小时了。谢谢。 - Raja Yogan
@calebisstupid:我也想看看你的解决方案,我已经尝试了多种方式来触发动画,但都没有成功。 - Lloyd
嗨!非常感谢您的解释和代码,它对我非常有用。我现在只是卡在了如何让容器在单击后消失这一问题上。我已经创建了一个新的问题来询问这个问题:https://dev59.com/ELLma4cB1Zd3GeqPWitP,希望您能帮助我 :) - Lambasoft
显示剩余16条评论

4
这解决了我的问题:
Future.delayed(const Duration(milliseconds: 20), () {

  setState(() {
    _pageController.animateToPage(0, duration: Duration(milliseconds: 1), curve: Curves.bounceIn);
  });

});

2
也为我解决了。我认为上面使用AnimationController的解决方案很好,但不确定如何将其集成到示例中,因为示例正在使用PageController。 - Rasmus Christensen

0

没有太多时间进行调查,但是AnimatedBuilder被调用了X次,等于可见children的数量。在第一帧中,尺寸不可用,因此controller.page会抛出Exception。计算第一帧动画偏移量的正确公式是(注意:这对我来说完美无缺,但请记住你自己的实现):

        builder: (context, child) {
          double value = _controller.position.haveDimensions
              ? _controller.page! - index
              : index.toDouble();
          value =  Curves.easeInOut.transform(
              (1 - (value.abs() * .4)).clamp(0.0, 1.0)
          );

          return Center(
            child: Container(
              height: value * widget.height,
              width: value * widget.width,
              padding: EdgeInsets.symmetric(horizontal: 15),
              child: child,
            ),
          );
        },

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接