🐮
【Flutter】バネのようなアニメーションをSpringSimulationで実現する
SpringSimulation とは
flutter/physics パッケージに含まれています。他には GravitySimulation などもあります。
SpringSimulation ではバネのような動きをシュミレートし、その動きをコントローラーを介しウィジェットに適用することができます。
SpringSimulationの設定例
SpringSimulation(
springDescription, // SpringDescription
_animationController.value, // start
1, // end
_animationController.velocity // velocity(速度)
)
SpringDescription とは
SpringSimulation でシュミレートするためのバネ係数の設定をします。
mass(質量)、stiffness(硬さ)、damping(減衰)が調整できますが、数値に対する動きの変化はとても複雑なので、値を少しずつ変化させながら試すと良いでしょう。
SpringDescriptionの設定例
SpringDescription(
mass: 1.0, // 質量
stiffness: 100.0, // 硬さ
damping: 10.0, // 減衰
);
実装してみる
まずはコントローラーの設定から。
upperBound を変更し、コントローラーの値範囲を 0.0 〜 1.2 とします。
これで、0.0 → 1.2 → 1.0 のような値の変化を可能にします。
1.0 → 1.2 の部分がバネで言う伸びている部分です。
controllerの設定
late final AnimationController _animationController;
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
upperBound: 1.2,
);
}
次に、アニメーションの実行関数を設定します。
SpringDescription でバネ係数の調整をし、SpringSimulation を作成します。
最後に、animateWith()でシュミレートに従いアニメーションを実行させます。
アニメーションの実行関数
void _animationForward() {
const springDescription = SpringDescription(
mass: 0.9,
stiffness: 190.0,
damping: 15.0,
);
final simulation =
SpringSimulation(springDescription, _animationController.value, 1, _animationController.velocity);
_animationController.animateWith(simulation);
}
Widget は AnimatedBuilder を使い動かします。
drive()で 0.0 → 80.0 と値を変化させ動かします。
AnimatedBuilder 横移動
AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
double t = _animationController.drive(Tween(begin: 0.0, end: 80.0)).value;
return Transform.translate(
offset: Offset(t, 0),
child: Container(
width: 100,
height: 50,
decoration: BoxDecoration(color: Colors.blue, borderRadius: BorderRadius.circular(25)),
),
);
},
)
_animationController.forward()で実行
_animationController.animateWith(simulation)で実行
実用例
このようなポップアップはよく見かけるのではないでしょうか。
実用例
AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
double h = _animationController.drive(Tween(begin: -100.0, end: 0.0)).value;
return Transform.translate(
offset: Offset(0, -h),
child: Container(
width: 200,
height: 100,
decoration: BoxDecoration(
color:
Colors.red.withOpacity(_animationController.value > 1 ? 1 : _animationController.value),
borderRadius: const BorderRadius.all(
Radius.circular(10.0),
),
),
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Attention!',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
color: Colors.white
.withOpacity(_animationController.value > 1 ? 1 : _animationController.value)),
),
Text(
'validation error',
style: TextStyle(
fontSize: 20.0,
color: Colors.white
.withOpacity(_animationController.value > 1 ? 1 : _animationController.value)),
),
],
)
),
)
);
},
)
Discussion