Flutter: アニメーションのウィジェットを使ってみる

Flutter: アニメーションのウィジェットを使ってみる

June 6, 2024

Flutter でアニメーションをするにはいろいろな方法がある。こちらの記事がわかりやすい。

上記記事中でも紹介されているが以下の動画が良き。

私なりにアニメーションを使ってみたのがこのような感じ。

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({
    super.key,
  });

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return const AnimatedColorWidget(
      child: MaterialApp(
        home: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {
  const MyWidget({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    final animatedColor = AnimatedColorWidget.of(context);

    return ColoredBox(
      color: animatedColor.color,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          IconButton(
            onPressed: animatedColor.toLightMode,
            icon: const Icon(Icons.light_mode),
          ),
          IconButton(
            onPressed: animatedColor.toDarkMode,
            icon: const Icon(Icons.dark_mode),
          ),
        ],
      ),
    );
  }
}

class InheritedColorWidget extends InheritedWidget {
  const InheritedColorWidget({
    super.key,
    required this.data,
    required super.child,
  });

  final AnimatedColorWidgetState data;

  @override
  bool updateShouldNotify(InheritedColorWidget oldWidget) {
    return data != oldWidget.data;
  }
}

class AnimatedColorWidget extends ImplicitlyAnimatedWidget {
  const AnimatedColorWidget({
    super.key,
    required this.child,
  }) : super(
          duration: const Duration(microseconds: 1000),
        );

  final Widget child;

  static AnimatedColorWidgetState of(
    BuildContext context, {
    bool listen = true,
  }) {
    final result = listen
        ? context.dependOnInheritedWidgetOfExactType<InheritedColorWidget>()
        : context
            .getElementForInheritedWidgetOfExactType<InheritedColorWidget>()
            ?.widget as InheritedColorWidget?;
    assert(result != null, 'No InheritedColorWidget found in context');
    return result!.data;
  }

  @override
  AnimatedColorWidgetState createState() => AnimatedColorWidgetState();
}

class AnimatedColorWidgetState
    extends AnimatedWidgetBaseState<AnimatedColorWidget> {
  Color _targetColor = Colors.white;

  Tween<Color>? _tweenColor = Tween<Color>(begin: Colors.white);

  Color get color => _tweenColor!.evaluate(animation);

  void toLightMode() {
    setState(() {
      _targetColor = Colors.white;
    });
  }

  void toDarkMode() {
    setState(() {
      _targetColor = Colors.black;
    });
  }

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
    _tweenColor = visitor(
      _tweenColor,
      _targetColor,
      (dynamic value) => Tween<Color>(begin: value as Color?),
    ) as Tween<Color>?;
  }

  @override
  Widget build(BuildContext context) => InheritedColorWidget(
        data: AnimatedColorWidgetState(),
        child: widget.child,
      );
}