Flutterでconfettiパッケージを使って紙吹雪を出す方法

image

Fultterで紙吹雪を出す方法を紹介します。

confettiDemo confettiDemo

Version

・Flutter 2.2.0
・Dart 2.13.0
・confetti 0.6.0-nullsafety

紙吹雪の出し方

confettiパッケージのインストール

まずconfettiをインストールします。pubspec.yamlに以下を追記し、次いでPub getをクリックします。

confetti: ^0.6.0-nullsafetys

そして紙吹雪を出したいファイルで以下を宣言します。

import 'package:confetti/confetti.dart';

コントローラを宣言

以下のようにコントローラを宣言します。

final _controller =
      ConfettiController(duration: const Duration(milliseconds: 500));

durationは紙吹雪を何秒出すかの設定です。例えばここをseconds: 10にしたら紙吹雪が10秒間出続けます。

ConfettiWidget()にコントローラを設定

ConfettiWidget()が紙吹雪を出すウィジェットになります。これをbuild関数の中に追加し、confettiControllerプロパティに先ほど宣言した_controllerを設定します。

ConfettiWidget(
              confettiController: _controller,
            )

紙吹雪を発動させるタイミングを設定

この1行が走れば紙吹雪が舞います。

_controller.play();

紙吹雪のカスタマイズ

ConfettiWidgetにはさまざまなプロパティがあります。お好みで設定して使いましょう。

噴出の仕方

blastDirectionalityプロパティにexplosivedirectionalのどちらかを設定します。
explosiveは爆発するように中央からランダムな方向に紙吹雪が噴出します。なので方向を指定する必要がありません。
directionalは一定方向に噴出します。こちらはblastDirectionで方向を指定する必要があります。

ConfettiWidget(
      confettiController: _controller,
      blastDirectionality: BlastDirectionality.explosive,
    );

噴出方向

blastDirectionプロパティに方向を設定します。
piはπのことです。0(2π)で右、π/2で下、πで左、3π/2で上。なぜか数学でやった単位円と逆まわりですね。

import 'dart:math';

ConfettiWidget(
      confettiController: _controller,
      blastDirectionality: BlastDirectionality.directional,
      blastDirection: pi / 2,
    );

放出頻度

emissionFrequencyプロパティで紙吹雪が放出される頻度を設定します。
0〜1の値にする必要があり、値が大きいほど紙吹雪が放出される頻度が増えます。

ConfettiWidget(
      confettiController: _controller,
      emissionFrequency: 1,
    );

一度に発射する紙の数

numberOfParticlesプロパティで一度に発射する紙の数を設定します。
デフォルトは10です。

ConfettiWidget(
      confettiController: _controller,
      numberOfParticles: 30,
    );

繰り返し

shouldLoopプロパティで繰り返すかどうかの設定をします。
デフォルトはfalseです。

ConfettiWidget(
      confettiController: _controller,
      shouldLoop: false,
    );

噴出初速の最大最小

maxBlastForceminBlastForceに噴出初速の最大値と最小値を設定します。
デフォルトは最大20、最小5です。

ConfettiWidget(
      confettiController: _controller,
      maxBlastForce: 5,
      minBlastForce: 2,
    );

発射位置の印の表示非表示

displayTargetをtrueにすることで紙吹雪の発射位置に印がついてわかりやすくなります。
デフォルトはfalseです。

ConfettiWidget(
      confettiController: _controller,
      displayTarget: true,
    );

colorsプロパティに色を設定できます。設定しなかった場合色はランダムになります。

ConfettiWidget(
      confettiController: _controller,
      colors: [Colors.green, Colors.pink,Colors.orange],
    );

サイズ

maximumSizeプロパティで紙吹雪のサイズの最大値を、minimumSizeプロパティで紙吹雪のサイズの最小値を設定します。
たとえば、minimumSizeをSize(10,10)、maximumSizeをSize(50,50)に設定すると、最小サイズ(10、10)と最大サイズ(50、50)[幅、高さ]の間のサイズの紙吹雪が作成されます。

ConfettiWidget(
      confettiController: _controller,
      maximumSize: Size(40, 40),
      minimumSize: Size(20, 20),
    );

紙の落ちる速さ

gravityプロパティに紙の落ちる速さを設定します。
0〜1の値にする必要があり、値が大きいほど速く落ちます。デフォルトは0.1です。

ConfettiWidget(
      confettiController: _controller,
      gravity: 0.3,
    );

抗力

particleDragプロパティに抗力を設定します。
抗力は空気抵抗みたいなイメージです。0〜1の値にする必要があります。値が大きいほど、散らばらずゆっくりと落ちていきます。デフォルトは0.05です。

ConfettiWidget(
      confettiController: _controller,
      particleDrag: 0.001,
    );

描画領域

canvasプロパティで紙吹雪が表示される領域のサイズを設定します。
デフォルトでは、フルスクリーンサイズに設定されています。

ConfettiWidget(
      confettiController: _controller,
      canvas: Size(100, 100),
    );

紙吹雪の形

createParticlePathプロパティに紙吹雪の形を設定できます。
デフォルトは長方形です。

ConfettiWidget(
      confettiController: _controller,
      createParticlePath: drawStar,
    );

カスタムcreateParticlePathの例

Path drawStar(Size size) {
    // Method to convert degree to radians
    double degToRad(double deg) => deg * (pi / 180.0);

    const numberOfPoints = 5;
    final halfWidth = size.width / 2;
    final externalRadius = halfWidth;
    final internalRadius = halfWidth / 2.5;
    final degreesPerStep = degToRad(360 / numberOfPoints);
    final halfDegreesPerStep = degreesPerStep / 2;
    final path = Path();
    final fullAngle = degToRad(360);
    path.moveTo(size.width, halfWidth);

    for (double step = 0; step < fullAngle; step += degreesPerStep) {
      path.lineTo(halfWidth + externalRadius * cos(step),
          halfWidth + externalRadius * sin(step));
      path.lineTo(halfWidth + internalRadius * cos(step + halfDegreesPerStep),
          halfWidth + internalRadius * sin(step + halfDegreesPerStep));
    }
    path.close();
    return path;
  }

コード全文

参考までにサンプルアプリのコードを載せておきます。

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _controller =
      ConfettiController(duration: const Duration(milliseconds: 500));

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('紙吹雪サンプル'),
      ),
      body: Stack(
        children: [
          Center(
            child: ElevatedButton(
              child: Text('紙吹雪舞わせる'),
              onPressed: () {
                _controller.play();
              },
            ),
          ),
          Container(
            padding: EdgeInsets.all(30),
            alignment: Alignment.topCenter,
            child: _buildConfetti(),
          ),
        ],
      ),
    );
  }

  Widget _buildConfetti() {
    return ConfettiWidget(
      confettiController: _controller,
      blastDirectionality: BlastDirectionality.explosive,
      // blastDirection: pi / 2,
      emissionFrequency: 1,
      numberOfParticles: 30,
      shouldLoop: false,
      maxBlastForce: 5,
      minBlastForce: 2,
      displayTarget: true,
      colors: [Colors.green, Colors.pink, Colors.orange],
      maximumSize: Size(40, 40),
      minimumSize: Size(20, 20),
      gravity: 0.3,
      particleDrag: 0.001,
      // canvas: Size(100, 100),
      createParticlePath: drawStar,
    );
  }

  Path drawStar(Size size) {
    // Method to convert degree to radians
    double degToRad(double deg) => deg * (pi / 180.0);

    const numberOfPoints = 5;
    final halfWidth = size.width / 2;
    final externalRadius = halfWidth;
    final internalRadius = halfWidth / 2.5;
    final degreesPerStep = degToRad(360 / numberOfPoints);
    final halfDegreesPerStep = degreesPerStep / 2;
    final path = Path();
    final fullAngle = degToRad(360);
    path.moveTo(size.width, halfWidth);

    for (double step = 0; step < fullAngle; step += degreesPerStep) {
      path.lineTo(halfWidth + externalRadius * cos(step),
          halfWidth + externalRadius * sin(step));
      path.lineTo(halfWidth + internalRadius * cos(step + halfDegreesPerStep),
          halfWidth + internalRadius * sin(step + halfDegreesPerStep));
    }
    path.close();
    return path;
  }
}

参考

https://pub.dev/packages/confetti/versions/0.6.0-nullsafety