3
votes

I'm trying to animate some widgets inside PageView and created a custom AnimatedWidget which uses PageController also used by PageView. The idea is to animate some text up as user swipes through slides. The code below will throw error on the first render, once I start swiping left/right everything works but I get red screen with error initially. I tried to defend by checking controller.hasClients but there is already 1 page in the controller and error comes from assertion in PageView page getter. Is there any other prop I can check to delay animating after the first render?

import 'dart:math' as math;

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

class MySlider extends StatelessWidget {
  final _controller = new PageController();

  final List<String> _pages = [
    'Create your football profile',
    'Your team, your players, your line-up',
    'Track the stats that matter and share with others'
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          PageView.builder(
            controller: _controller,
            itemBuilder: (_, index) => Slide(
              controller: _controller,
              position: index,
              heading: _pages[index],
            ),
            itemCount: _pages.length,
            physics: AlwaysScrollableScrollPhysics(),
          ),
        ]
      )
    );
  }
}

class Slide extends AnimatedWidget {

  final PageController controller;
  final String heading;
  final int position;

  Slide({
    this.controller,
    this.heading,
    this.position
  }) : super(listenable: controller);

  @override
  Widget build(BuildContext context) {

    final screenHeight = MediaQuery.of(context).size.height;
    final textYOffset = controller.hasClients
        ? screenHeight * (controller.page - position)
        : 0;

    final opacity = controller.hasClients
        ? 1.0 - math.max(0, controller.page - position)
        : 1.0;

    return Stack(
        alignment: Alignment.topCenter,
        children: [
          Positioned(
              bottom: 120,
              left: 16,
              right: 16,
              child: Transform.translate(
                  offset: Offset(0, -textYOffset),
                  child: Opacity(
                      opacity: opacity,
                      child: Text(heading)
                  ),
              ),
          )
        ],
    );
  }
}

Exception in the console:

════════ Exception caught by widgets library ═════════════════════════════════════════════

The following assertion was thrown building Slide(animation:

PageController#c35ec(one client, offset 0.0), dirty, dependencies: [MediaQuery], state: _AnimatedState#05ff4):

Page value is only available after content dimensions are established. 'package:flutter/src/widgets/page_view.dart':

Failed assertion: line 373 pos 7: 'pixels == null || (minScrollExtent != null &&maxScrollExtent != null)'

minScrollExtent at this point is 0.0 same with maxScrollExtent

1

1 Answers

3
votes

Taking into account the way you implemented the slide and the error you are getting about the content dimensions not being available a possible, but I think ugly, solution would be to check if the dimensions are available before trying to use the controller:

if(controller.position.haveDimensions){
  final screenHeight = MediaQuery.of(context).size.height;
  final textYOffset = controller.hasClients
      ? screenHeight * (controller.page - position)
      : 0;

  final opacity = controller.hasClients
      ? 1.0 - math.max(0, controller.page - position)
      : 1.0;

  return Stack(
    alignment: Alignment.topCenter,
    children: [
      Positioned(
        bottom: 120,
        left: 16,
        right: 16,
        child: Transform.translate(
          offset: Offset(0, -textYOffset),
          child: Opacity(
              opacity: opacity,
              child: Text(heading)
          ),
        ),
      )
    ],
  );
} else {
  return SizedBox();
}

Despite this "fixing" the error you get displayed, you still have an error on the last page, regardless of my changes. There might be a better implementation of the animation you want to have, but I haven't figured it out yet.