4

I have a PageView with 4 pages, set up like so:

PageView(
  children: [
      _buildPage(color: Colors.orange[600]),
      _buildPage(color: Colors.deepPurple[400]),
      _buildPage(color: Colors.yellow[300]),
      _buildPage(color: Colors.blue[400]),
    ],
  ),

Inside each page, I have a container with the color I passed in as a parameter:

Widget _buildPage({color: Color}) {
  return Container(
    color: color,
    ...

Whenever I swipe across the pages, there is a hard transition between the colors enter image description here

I want to have it so that whenever I swipe across the pages, the color from one fades as a gradient into the color from the newer page. Is it possible to do that? I'm very new to Flutter and I haven't been able to find anything on this.

EDIT: This is how I want them to look like.

Swiping across quickly.
And slowly.

I'm adding gfycat links, as the downloaded gifs were playing too slowly.

lepsch
  • 8,927
  • 5
  • 24
  • 44
Rafael Uchoa
  • 115
  • 3
  • 10

2 Answers2

16

You can use a TweenSequence to transition between multiple tween. Combined with ColorTween to define a color transition.

Then you can wrap it all using AnimatedBuilder by listening to your PageController.

enter image description here

class Home extends StatefulWidget {
  @override
  HomeState createState() {
    return new HomeState();
  }
}

class HomeState extends State<Home> {
  PageController pageController;
  Animatable<Color> background;

  @override
  void initState() {
    _initialize();
    super.initState();
  }

  void _initialize() {
    background = TweenSequence<Color>([
      TweenSequenceItem(
        weight: 1.0,
        tween: ColorTween(
          begin: Colors.orange[600],
          end: Colors.deepPurple[400],
        ),
      ),
      TweenSequenceItem(
        weight: 1.0,
        tween: ColorTween(
          begin: Colors.deepPurple[400],
          end: Colors.yellow[300],
        ),
      ),
      TweenSequenceItem(
        weight: 1.0,
        tween: ColorTween(
          begin: Colors.yellow[300],
          end: Colors.blue[400],
        ),
      ),
    ]);
    pageController = PageController();
  }

  @override
  void reassemble() {
    pageController.dispose();
    _initialize();
    super.reassemble();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: AnimatedBuilder(
        animation: pageController,
        builder: (context, child) {
          final color = pageController.hasClients ? pageController.page / 3 : .0;

          return DecoratedBox(
            decoration: BoxDecoration(
              color: background.evaluate(AlwaysStoppedAnimation(color)),
            ),
            child: child,
          );
        },
        child: PageView(
          controller: pageController,
          children: [
            Center(child: Text("Orange")),
            Center(child: Text("Purple")),
            Center(child: Text("Lime")),
            Center(child: Text("Blue")),
          ],
        ),
      ),
    );
  }
}
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • Do you know what needs to be imported? I keep getting errors that TweenSequence and TweenSequenceItem are not defined. I tried importing the animations package, but that didn't do it. – Rafael Uchoa Aug 20 '18 at 16:42
  • 1
    The usual stuff. But this is a new feature. You may have to upgrade flutter or even switch to master branch – Rémi Rousselet Aug 20 '18 at 16:45
  • can you explain bit more, why `pageController.page / 3` this? – rahul Kushwaha May 14 '20 at 15:32
  • Because there are 4 pages with a pagescrool of three times of the width (the first page has a pageController.page =0, second page has the value pageController.page =1 or one width and so one). So if you have lets say 7 pages you need to devide by 6 (n-1) – Georg Panteleev Jan 24 '21 at 03:08
1

Updated code to Flutter 3.x


This is the same code from Rémi Rousselet's answer but updated to run on Flutter 3.x and as a minimal-reproducible-example.

Live demo on DartPad

enter image description here

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const Home(),
      scrollBehavior: MyCustomScrollBehavior(),
      debugShowCheckedModeBanner: false,
    );
  }
}

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

  @override
  HomeState createState() {
    return HomeState();
  }
}

class HomeState extends State<Home> {
  late final PageController pageController;
  late final Animatable<Color?> background;

  @override
  void initState() {
    _initialize();
    super.initState();
  }

  void _initialize() {
    background = TweenSequence<Color?>([
      TweenSequenceItem(
        weight: 1.0,
        tween: ColorTween(
          begin: Colors.orange[600],
          end: Colors.deepPurple[400],
        ),
      ),
      TweenSequenceItem(
        weight: 1.0,
        tween: ColorTween(
          begin: Colors.deepPurple[400],
          end: Colors.yellow[300],
        ),
      ),
      TweenSequenceItem(
        weight: 1.0,
        tween: ColorTween(
          begin: Colors.yellow[300],
          end: Colors.blue[400],
        ),
      ),
    ]);
    pageController = PageController();
  }

  @override
  void reassemble() {
    pageController.dispose();
    _initialize();
    super.reassemble();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: AnimatedBuilder(
        animation: pageController,
        builder: (context, child) {
          final color =
              pageController.hasClients ? pageController.page! / 3 : .0;

          return DecoratedBox(
            decoration: BoxDecoration(
              color: background.evaluate(AlwaysStoppedAnimation(color)),
            ),
            child: child,
          );
        },
        child: PageView(
          controller: pageController,
          children: const [
            Center(child: Text("Orange")),
            Center(child: Text("Purple")),
            Center(child: Text("Lime")),
            Center(child: Text("Blue")),
          ],
        ),
      ),
    );
  }
}

// Enables scrolling with mouse dragging
class MyCustomScrollBehavior extends MaterialScrollBehavior {
  @override
  Set<PointerDeviceKind> get dragDevices => {
        PointerDeviceKind.touch,
        PointerDeviceKind.mouse,
      };
}
lepsch
  • 8,927
  • 5
  • 24
  • 44