Shifenis
Shifenis

Reputation: 1125

Animate scaffold background color

like the title I'm trying to change with animation the Scaffold background-color. On onPageChanged PageView I trigger the animation. So far so good. The problem is with more colors. I mean, I have to take the colors from an API and when the user scrolls the card (PageView) I have to fit the color of that card.
Any suggestions?

This is the current code (with preview) for 2 colors: DartPad.

Thanks in advance :)

Upvotes: 0

Views: 2983

Answers (2)

Faiz
Faiz

Reputation: 6980

full example

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'stackoverflow question',
      home: SafeArea(
        top: false,
        bottom: true,
        child: HomepageScreen(),
      ),
    );
  }
}

class HomepageScreen extends StatefulWidget {
  const HomepageScreen({Key? key}) : super(key: key);

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

class _HomepageScreenState extends State<HomepageScreen>
    with SingleTickerProviderStateMixin {
  AnimationController? _controller;
  Animation<Color>? animation;
  
  final colors = <TweenSequenceItem<Color>>[
      TweenSequenceItem(
        weight: 1.0,
        tween: Tween<Color>(begin: Colors.red, end: Colors.blue),
      ),
      TweenSequenceItem(
        weight: 1.0,
        tween: Tween<Color>(begin: Colors.blue, end: Colors.green),
      ),
      TweenSequenceItem(
        weight: 1.0,
        tween: Tween<Color>(begin: Colors.green, end: Colors.yellow),
      ),
      TweenSequenceItem(
        weight: 1.0,
        tween: Tween<Color>(begin: Colors.yellow, end: Colors.red),
      ),
    ];

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 600),
      vsync: this,
    );

    animation = TweenSequence<Color>(colors).animate(_controller!)..addListener(() {
        setState(() {});
      });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: animation!.value,
      body: Container(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Expanded(
              child: PageView.builder(
                physics: new PageScrollPhysics(),
                itemCount: colors.length,
                controller: PageController(viewportFraction: 0.8),
                onPageChanged: ((int index) {
                  _controller!.animateTo(index / colors.length);
                }),
                itemBuilder: (_, i) {
                  return Padding(
                    padding:
                        EdgeInsets.only(left: i % 2 == 0 ? 0 : 15, bottom: 20),
                    child: Container(),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Upvotes: 1

Michael Pfaff
Michael Pfaff

Reputation: 1263

Use a TweenSequence<Color>

Assuming the weight of each color in the sequence is the same, you can find a given color with index / colors.length as I show in the onPageChanged callback.

Given this, _controller.animateTo(index / colors.length) will take the duration specified in the AnimationController to animation in either direction to from the current color to the new color.

Here's a live demo

Here's the relevant code (only showing what was changed from yours). I changed the duration and animation physics for personal preference. By all means, use what you prefer.

class _HomepageScreenState extends State<HomepageScreen>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation<Color> animation;

  final colors = <TweenSequenceItem<Color>>[
      TweenSequenceItem(
        weight: 1.0,
        tween: ColorTween(begin: Colors.red, end: Colors.blue),
      ),
      TweenSequenceItem(
        weight: 1.0,
        tween: ColorTween(begin: Colors.blue, end: Colors.green),
      ),
      TweenSequenceItem(
        weight: 1.0,
        tween: ColorTween(begin: Colors.green, end: Colors.yellow),
      ),
      TweenSequenceItem(
        weight: 1.0,
        tween: ColorTween(begin: Colors.yellow, end: Colors.red),
      ),
    ];

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 600),
      vsync: this,
    );

    animation = TweenSequence<Color>(colors).animate(_controller)..addListener(() {
        setState(() {});
      });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: animation.value,
      body: Container(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Expanded(
              child: PageView.builder(
                physics: new PageScrollPhysics(),
                itemCount: colors.length,
                controller: PageController(viewportFraction: 0.8),
                onPageChanged: ((int index) {
                  _controller.animateTo(index / colors.length);
                }),
                itemBuilder: (_, i) {
                  return Padding(
                    padding:
                        EdgeInsets.only(left: i % 2 == 0 ? 0 : 15, bottom: 20),
                    child: Container(),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Upvotes: 4

Related Questions