带有条件小部件的Flutter Hero交易容器

问题描述 投票:1回答:2

我正在尝试实现顺畅的英雄交易,但是我要转换的容器有两个变体(小/大)。

大:

big container

小:

small container

您可以看到,小版本与大版本相同,但是缺少一些元素。使用属性isSmall设置需要呈现的版本。

该组件如下所示:

class TicPackage extends StatelessWidget {
  TicPackage({this.package, this.onTap, this.isSmall = false});

  final Package package;
  final bool isSmall;
  final Function() onTap;

  final NumberFormat currencyFormatter =
      NumberFormat.currency(locale: "nl", decimalDigits: 2, symbol: "€");

  @override
  Widget build(BuildContext context) {
    Widget titleText = Text(
      package.name,
      style: TextStyle(
          color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold),
    );

    return TicCard(
      color: package.color,
      elevation: 4,
      onTap: onTap,
      children: <Widget>[
        Row(
          children: <Widget>[
            isSmall
                ? titleText
                : Text("${package.eventCount} evenementen",
                    style:
                        TextStyle(color: Color.fromRGBO(255, 255, 255, 0.5))),
            Text(
              "${currencyFormatter.format(package.price)}",
              style: TextStyle(
                  color: Colors.white,
                  fontSize: 22,
                  fontWeight: FontWeight.bold),
            ),
          ],
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
        ),
        if (!isSmall)
          Padding(padding: EdgeInsets.only(top: 10), child: titleText),
        Padding(
            padding: EdgeInsets.only(top: 2),
            child: Text(package.description,
                style: TextStyle(color: Colors.white))),
        if (!isSmall)
          Padding(
              padding: EdgeInsets.only(top: 12),
              child: Text(package.goods,
                  style: TextStyle(
                      color: Colors.white, fontStyle: FontStyle.italic))),
        if (!isSmall)
          Padding(
              padding: EdgeInsets.only(top: 10),
              child: Container(
                child: Padding(
                    padding: EdgeInsets.symmetric(horizontal: 10, vertical: 3),
                    child: Text(
                      "${currencyFormatter.format(package.discount)} korting",
                      style: TextStyle(color: Colors.white),
                    )),
                decoration: BoxDecoration(
                    border:
                        Border.all(color: Color.fromRGBO(255, 255, 255, 0.5)),
                    borderRadius: BorderRadius.circular(100)),
              ))
      ],
    );
  }
}

屏幕A:

Hero(
    tag: "package_${args.package.id}",
    child: TicPackage(
      isSmall: false,
      package: args.package
)))

屏幕B:

Hero(
    tag: "package_${args.package.id}",
    child: TicPackage(
      isSmall: true,
      package: args.package
)))

现在过渡看起来如下:

hero transition

您可以看到它运行良好,但是由于我在这里使用条件渲染,因此有点活泼。同样,向后过渡也会产生错误:

A RenderFlex overflowed by 96 pixels on the bottom.

我猜这是因为在返回的途中,空间突然溢出,因为这些多余的小部件正在被渲染。

现在,我的问题是如何正确创建需要使用条件元素进行转换的英雄组件。或者,如果hero小部件不适合此操作,我如何通过做一些自定义动画来达到相同的结果?

flutter flutter-animation
2个回答
1
投票

用SingleChildScrollView将列包裹在TicCard中

enter image description here

导入'package:flutter / material.dart';

import 'page2.dart';

class TicCard extends StatelessWidget {
  final List<Widget> children;
  final double elevation;
  final Color color;

  const TicCard({
    Key key,
    this.children,
    this.elevation,
    this.color,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => Navigator.of(context).push(
        MaterialPageRoute(
          builder: (_) => Page2(),
        ),
      ),
      child: Card(
        elevation: elevation,
        color: color,
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: SingleChildScrollView(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: children,
            ),
          ),
        ),
      ),
    );
  }
}

0
投票

使用flightShuttleBuilder。在此构建器中,创建一个采用TicCard动画的新hero。您现在可以使用此animationflight(屏幕过渡)期间为所有视图设置动画。

我不满意的一件事是_animationWidget。它的作用:将所有小部件包装在FadeTransitionSizeTransition内,如果没有animationisSmall为true,则返回空的Container

代码:

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:intl/intl.dart';
import 'package:ticketapp_pakket/components/tic-card.dart';
import 'package:ticketapp_pakket/models/package.dart';

class TicPackage extends StatelessWidget {
  TicPackage(
      {this.heroTag,
      this.package,
      this.onTap,
      this.isSmall = false,
      this.animation});

  final String heroTag;
  final Animation<double> animation;
  final Package package;
  final bool isSmall;
  final Function() onTap;

  final NumberFormat currencyFormatter =
      NumberFormat.currency(locale: "nl", decimalDigits: 2, symbol: "€");

  Widget _animationWidget({Widget child}) {
    return animation != null
        ? FadeTransition(
            opacity: animation,
            child: SizeTransition(
                axisAlignment: 1.0, sizeFactor: animation, child: child))
        : !isSmall ? child : Container();
  }

  @override
  Widget build(BuildContext context) {
    Widget eventCountText = _animationWidget(
        child: Padding(
            padding: EdgeInsets.only(bottom: 10),
            child: Text("${package.eventCount} evenementen",
                style: TextStyle(color: Color.fromRGBO(255, 255, 255, 0.5)))));

    Widget goodsText = _animationWidget(
      child: Padding(
          padding: EdgeInsets.only(top: 12),
          child: Text(package.goods,
              style:
                  TextStyle(color: Colors.white, fontStyle: FontStyle.italic))),
    );

    Widget discountText = _animationWidget(
        child: Padding(
            padding: EdgeInsets.only(top: 10),
            child: Container(
              child: Padding(
                  padding: EdgeInsets.symmetric(horizontal: 10, vertical: 3),
                  child: Text(
                    "${currencyFormatter.format(package.discount)} korting",
                    style: TextStyle(color: Colors.white),
                  )),
              decoration: BoxDecoration(
                  border: Border.all(color: Color.fromRGBO(255, 255, 255, 0.5)),
                  borderRadius: BorderRadius.circular(100)),
            )));

    Widget titleText = Text(
      package.name,
      style: TextStyle(
          color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold),
    );

    Widget card = TicCard(
        color: package.color,
        borderRadius: BorderRadius.circular(10),
        margin: EdgeInsets.only(left: 20, right: 20, bottom: 10, top: 5),
        onTap: onTap,
        child: Container(
          padding: EdgeInsets.all(15),
          child: Stack(
            children: <Widget>[
              Column(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  eventCountText,
                  titleText,
                  Padding(
                      padding: EdgeInsets.only(top: 2),
                      child: Text(package.description,
                          style: TextStyle(color: Colors.white))),
                  goodsText,
                  discountText,
                ],
              ),
              Positioned(
                  child: Text(
                    "${currencyFormatter.format(package.price)}",
                    style: TextStyle(
                        color: Colors.white,
                        fontSize: 22,
                        fontWeight: FontWeight.bold),
                  ),
                  top: 0,
                  right: 0)
            ],
          ),
        ));

    if (heroTag == null) {
      return card;
    }

    return Hero(
        tag: heroTag,
        flightShuttleBuilder: (
          BuildContext flightContext,
          Animation<double> animation,
          HeroFlightDirection flightDirection,
          BuildContext fromHeroContext,
          BuildContext toHeroContext,
        ) {
          return TicPackage(
            package: package,
            animation: ReverseAnimation(animation),
          );
        },
        child: card);
  }
}

结果:

result

导致慢动作:

result in slow motion

© www.soinside.com 2019 - 2024. All rights reserved.