enter_exit_transition.dart 4.63 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:flutter/animation.dart';

import 'basic.dart';
import 'framework.dart';

class SmoothlyResizingOverflowBox extends StatefulComponent {
  SmoothlyResizingOverflowBox({
    Key key,
    this.child,
    this.size,
    this.duration,
    this.curve: Curves.linear
  }) : super(key: key) {
    assert(duration != null);
    assert(curve != null);
  }

  final Widget child;
  final Size size;
  final Duration duration;
  final Curve curve;

  _SmoothlyResizingOverflowBoxState createState() => new _SmoothlyResizingOverflowBoxState();
}

class _SmoothlyResizingOverflowBoxState extends State<SmoothlyResizingOverflowBox> {
  ValuePerformance<Size> _size;

  void initState() {
    super.initState();
    _size = new ValuePerformance(
      variable: new AnimatedSizeValue(config.size, curve: config.curve),
      duration: config.duration
    )..addListener(() {
      setState(() {});
    });
  }

  void didUpdateConfig(SmoothlyResizingOverflowBox oldConfig) {
    _size.duration = config.duration;
    _size.variable.curve = config.curve;
    if (config.size != oldConfig.size) {
      AnimatedSizeValue variable = _size.variable;
      variable.begin = variable.value;
      variable.end = config.size;
      _size.progress = 0.0;
      _size.play();
    }
  }

  void dispose() {
    _size.stop();
    super.dispose();
  }

  Widget build(BuildContext context) {
    return new SizedOverflowBox(
      size: _size.value,
      child: config.child
    );
  }
}

class _Entry {
  _Entry({
    this.child,
    this.enterPerformance,
    this.enterTransition
  });

  final Widget child;
  final Performance enterPerformance;
  final Widget enterTransition;

  Size childSize = Size.zero;

  Performance exitPerformance;
  Widget exitTransition;

  Widget get currentTransition => exitTransition ?? enterTransition;

  void dispose() {
    enterPerformance?.stop();
    exitPerformance?.stop();
  }
}

typedef Widget TransitionBuilderCallback(PerformanceView performance, Widget child);

96 97
Widget _identityTransition(PerformanceView performance, Widget child) => child;

98 99 100 101 102 103
class EnterExitTransition extends StatefulComponent {
  EnterExitTransition({
    Key key,
    this.child,
    this.duration,
    this.curve: Curves.linear,
104 105
    this.onEnter: _identityTransition,
    this.onExit: _identityTransition
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
  }) : super(key: key) {
    assert(child != null);
    assert(duration != null);
    assert(curve != null);
    assert(onEnter != null);
    assert(onExit != null);
  }

  final Widget child;
  final Duration duration;
  final Curve curve;
  final TransitionBuilderCallback onEnter;
  final TransitionBuilderCallback onExit;

  _EnterExitTransitionState createState() => new _EnterExitTransitionState();
}

class _EnterExitTransitionState extends State<EnterExitTransition> {
  final List<_Entry> _entries = new List<_Entry>();

  void initState() {
    super.initState();
    _entries.add(_createEnterTransition());
  }

  _Entry _createEnterTransition() {
    Performance enterPerformance = new Performance(duration: config.duration)..play();
    return new _Entry(
      child: config.child,
      enterPerformance: enterPerformance,
      enterTransition: config.onEnter(enterPerformance, new KeyedSubtree(
        key: new GlobalKey(),
        child: config.child
      ))
    );
  }

  Future _createExitTransition(_Entry entry) async {
    Performance exitPerformance = new Performance(duration: config.duration);
    entry
      ..exitPerformance = exitPerformance
      ..exitTransition = config.onExit(exitPerformance, entry.enterTransition);
    await exitPerformance.play();
    if (!mounted)
      return;
    setState(() {
      _entries.remove(entry);
    });
  }

  void didUpdateConfig(EnterExitTransition oldConfig) {
    if (config.child.key != oldConfig.child.key) {
      _createExitTransition(_entries.last);
      _entries.add(_createEnterTransition());
    }
  }

  void dispose() {
    for (_Entry entry in new List<_Entry>.from(_entries))
      entry.dispose();
    super.dispose();
  }

  Widget build(BuildContext context) {
    return new SmoothlyResizingOverflowBox(
      size: _entries.last.childSize,
      duration: config.duration,
      curve: config.curve,
      child: new Stack(_entries.map((_Entry entry) {
        return new SizeObserver(
          key: new ObjectKey(entry),
          onSizeChanged: (Size newSize) {
            setState(() {
              entry.childSize = newSize;
            });
          },
          child: entry.currentTransition
        );
      }).toList())
    );
  }
}