skip to Main Content

I’m writing a custom bar-chart and using AnimatedContainer for the vertical bars. I only have one render, and it does not animate. To remedy this, I have set the initial height to zero for all bars and forced a rebuild. This works, but it is a little messy. Is there another way to force the animation without a rebuild?

Relevant code is:

AnimatedContainer /* BAR-CHART-ITEM */ (
  duration: const Duration(seconds: 1),
  curve: Curves.easeOut,
  width: widget.clsBarChart.dBarWidth,
  height: widget.isFirst
      ? 0
      : widget.dHeightScrollBarMax *
          widget.clsBarChart.lsdHeightFactors[widget.iNdxItem],

2

Answers


  1. You’re right, forcing a rebuild for animation can be inefficient.

    Here are a couple of ways to achieve animation without a rebuild in your custom bar chart:

    AnimatedContainer(
      duration: const Duration(seconds: 1),
      curve: Curves.easeOut,
      width: widget.clsBarChart.dBarWidth,
      child: TweenAnimationBuilder(
        tween: Tween<double>(begin: 0.0, end: widget.isFirst ? 0.0 : widget.dHeightScrollBarMax * widget.clsBarChart.lsdHeightFactors[widget.iNdxItem]),
        duration: const Duration(seconds: 1),
        builder: (context, value, child) => Container(
          height: value, // Dynamically set height based on animation
          color: Colors.blue, // Add your bar color here
        ),
      ),
    ),
    

    Another option is to use a Stack and animate the visibility of two widgets: one with zero height and the actual bar with its calculated height. You can use a FadeTransition or a SizeTransition to animate the appearance of the desired bar.

    Stack(
      children: [
        AnimatedPositioned(
          duration: const Duration(seconds: 1),
          curve: Curves.easeOut,
          bottom: 0,
          child: Container(
            height: 0.0, // Always zero height
            width: widget.clsBarChart.dBarWidth,
            color: Colors.transparent,
          ),
        ),
        AnimatedPositioned(
          duration: const Duration(seconds: 1),
          curve: Curves.easeOut,
          bottom: 0,
          child: FadeTransition(
            opacity: widget.isFirst ? 0.0 : 1.0,
            child: Container(
              height: widget.dHeightScrollBarMax * widget.clsBarChart.lsdHeightFactors[widget.iNdxItem],
              width: widget.clsBarChart.dBarWidth,
              color: Colors.blue, // Add your bar color here
            ),
          ),
        ),
      ],
    ),
    
    Login or Signup to reply.
  2. You cannot use Animated Container directly to create vertical bar because Animated Container doesn’t initiate animation in first render instead it follow second render to initiate animation.

    Instead you can follow the bellow approach for similar effect.

     class AnimatedBar extends StatefulWidget {
          final double finalHeight;
          final Color color;
        
          const AnimatedBar({Key? key, required this.finalHeight, required this.color}) : super(key: key);
        
          @override
          _AnimatedBarState createState() => _AnimatedBarState();
        }
        
        class _AnimatedBarState extends State<AnimatedBar> with TickerProviderStateMixin {
          //AnimationController and Animation for animating the bar height;
          AnimationController? _controller;
          Animation<double>? _animation;
        
          // flag to track if the animation has already played.
          bool _hasAnimated = false;
        
          @override
          void initState() {
            super.initState();
            _controller = AnimationController(duration: Duration(milliseconds: 500), vsync: this);
            _animation = Tween<double>(begin: 0.0, end: widget.finalHeight).animate(_controller!);
            if (!_hasAnimated) {
             // Trigger the animation forward only on the first render using a _hasAnimated boolean flag.
              _controller!.forward();
              _hasAnimated = true;
            }
          }
        
          @override
          void dispose() {
            _controller?.dispose();
            super.dispose();
          }
        
          @override
      Widget build(BuildContext context) {
        return AnimatedBuilder(
            animation: _animation!,
            builder: (context, child) => Column(
                  mainAxisSize: MainAxisSize.max,
                  mainAxisAlignment: MainAxisAlignment.end,
                  children: [
                    Container(
                      height: _animation!.value,
                      width: 10.0, // Adjust width as needed
                      color: widget.color,
                    ),
                  ],
                ));
      }
    }
    

    You can use this Animated Bar with this below sample code.

    class Chart extends StatefulWidget {
      const Chart({super.key});
    
      @override
      State<Chart> createState() => _ChartState();
    }
    
    class _ChartState extends State<Chart> {
      List<double> barHeights = [50.0, 80.0, 30.0];
      List<Color> barColors = [Colors.red, Colors.green, Colors.blue];
    
      @override
      Widget build(BuildContext context) {
        return Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            for (int i = 0; i < barHeights.length; i++)
              AnimatedBar(finalHeight: barHeights[i], color: barColors[i]),
          ],
        );
      }
    }
    
    class ScreenDemo extends StatelessWidget {
      const ScreenDemo({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(appBar: AppBar(),body: Scaffold(
            body: Container(
                child: Chart())));
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search