LoginSignup
5
0

More than 3 years have passed since last update.

[Flutter] animations の OpenContainer で少しハマったところ

Posted at

この記事は

FlutterのPackageであるanimationsを使った時に少しハマったことがあったので書きました

経緯

比較的リッチなアニメーションを簡単に実装できるanimationsを試してみたく、exampleを参考に以下のようなコードを書きました。

main_screen.dart
class MainScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF7F7F8),
      appBar: AppBar(
        title: const Text('猫ちゃん'),
      ),
      body: ListView(
        children: <Widget>[
          _OpenContainerWrapper(
            openPage: const CatPage(),
            closedBuilder: (BuildContext _, VoidCallback openContainer) {
              return ReusableCard(
                imagePath: 'assets/images/cat.jpg',
                contentTitle: '猫ちゃん',
                onPress: openContainer,
              );
            },
          ),
        ],
      ),
    );
  }
}

class _OpenContainerWrapper extends StatelessWidget {
  const _OpenContainerWrapper({
    this.closedBuilder,
    this.openPage,
  });

  final OpenContainerBuilder closedBuilder;
  final Widget openPage;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.fromLTRB(8, 8, 8, 0),
      child: OpenContainer(
        transitionType: ContainerTransitionType.fade,
        openBuilder: (BuildContext context, VoidCallback _) {
          return openPage;
        },
        tappable: false,
        closedBuilder: closedBuilder,
      ),
    );
  }
}

Itemをタップすることで、アニメーションしながらCatPage()へ遷移する想定です。
ここで私は「ListViewの子には角丸のCardを使いたいから、ReusableCardCard()を使うべきだろう」と安直に考えて、以下のように書きました。

reusable_card.dart
class ReusableCard extends StatelessWidget {
  const ReusableCard({
    @required this.imagePath,
    @required this.contentTitle,
    @required this.onPress,
  });
  final String imagePath;
  final String contentTitle;
  final VoidCallback onPress;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: InkWell(
        onTap: onPress,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Image.asset(
              imagePath,
              fit: BoxFit.fill,
            ),
            Padding(
              padding: const EdgeInsets.fromLTRB(16, 24, 0, 24),
              child: Text(
                contentTitle,
                style: Theme.of(context).textTheme.bodyText2.copyWith(
                      fontWeight: FontWeight.bold,
                    ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

そしてbuildしてみると、、、

Simulator Screen Shot - iPhone SE (2nd generation) - 2020-06-24 at 10.54.17.png

なぜCardが二重に。。。

原因

原因はOpenContainerclosedShapeでした。

open_container.dart
  const OpenContainer({
    Key key,
    this.closedColor = Colors.white,
    this.openColor = Colors.white,
    this.closedElevation = 1.0,
    this.openElevation = 4.0,
    // ↓これ
    this.closedShape = const RoundedRectangleBorder(
      borderRadius: BorderRadius.all(Radius.circular(4.0)),
    ),
    this.openShape = const RoundedRectangleBorder(),
    @required this.closedBuilder,
    @required this.openBuilder,
    this.tappable = true,
    this.transitionDuration = const Duration(milliseconds: 300),
    this.transitionType = ContainerTransitionType.fade,
  })  : assert(closedColor != null),
        assert(openColor != null),
        assert(closedElevation != null),
        assert(openElevation != null),
        assert(closedShape != null),
        assert(openShape != null),
        assert(closedBuilder != null),
        assert(openBuilder != null),
        assert(tappable != null),
        assert(transitionType != null),
        super(key: key);

closedShapeとは、タップアニメーション前の形(この場合ListのItemの形)を指定するパラメータです。
closedShapeにはデフォルトで角丸4dpのRoundedRectangleBorderが指定されており、その子にCardを入れていたがために、角丸のものが二重で配置されてしまったという訳でした。

今回の場合は、ReusableCardのCard部分を取り除いてあげれば解決です。

reusable_card.dart
class ReusableCard extends StatelessWidget {
  const ReusableCard({
    @required this.imagePath,
    @required this.contentTitle,
    @required this.onPress,
  });
  final String imagePath;
  final String contentTitle;
  final VoidCallback onPress;

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: onPress,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          Image.asset(
            imagePath,
            fit: BoxFit.fill,
          ),
          Padding(
            padding: const EdgeInsets.fromLTRB(16, 24, 0, 24),
            child: Text(
              contentTitle,
              style: Theme.of(context).textTheme.bodyText2.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
            ),
          ),
        ],
      ),
    );
  }
}

Simulator Screen Shot - iPhone SE (2nd generation) - 2020-06-24 at 10.54.47.png

別のshapeにする際は、closedShapeへ明示的に指定してあげましょう。

終わりに

今思えば何故こんな単純な間違いに気づかなかったのかと恥ずかしい気持ちですが、同じようにつまづいた方がいるかもしれないので、その時の助けになれば幸いです。

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0