progress_indicator.dart 4.78 KB
Newer Older
1 2 3 4 5 6 7
// 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:math' as math;
import 'dart:sky' as sky;

8
import 'package:sky/animation.dart';
9 10 11 12
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/theme.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/transitions.dart';
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

const double _kLinearProgressIndicatorHeight = 6.0;
const double _kMinCircularProgressIndicatorSize = 15.0;
const double _kCircularProgressIndicatorStrokeWidth = 3.0;

abstract class ProgressIndicator extends StatefulComponent {
  ProgressIndicator({
    Key key,
    this.value,
    this.bufferValue
  }) : super(key: key);

  double value; // Null for non-determinate progress indicator.
  double bufferValue; // TODO(hansmuller) implement the support for this.

28 29
  AnimationPerformance _performance;
  double get _performanceValue => (_performance.variable as AnimatedValue<double>).value;
30 31
  Color get _backgroundColor => Theme.of(this).primarySwatch[200];
  Color get _valueColor => Theme.of(this).primaryColor;
32
  Object get _customPaintToken => value != null ? value : _performanceValue;
33 34

  void initState() {
35
    _performance = new AnimationPerformance()
36 37
      ..duration = const Duration(milliseconds: 1500)
      ..variable = new AnimatedValue<double>(0.0, end: 1.0, curve: ease);
38 39 40 41 42
    _performance.addStatusListener((AnimationStatus status) {
      if (status == AnimationStatus.completed)
        _restartAnimation();
    });
    _performance.play();
43 44
  }

45
  void syncConstructorArguments(ProgressIndicator source) {
46 47 48 49 50
    value = source.value;
    bufferValue = source.bufferValue;
  }

  void _restartAnimation() {
51 52
    _performance.progress = 0.0;
    _performance.play();
53 54 55 56 57 58 59
  }

  Widget build() {
    if (value != null)
      return _buildIndicator();

    return new BuilderTransition(
60 61
      variables: [_performance.variable],
      performance: _performance.view,
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
      builder: _buildIndicator
    );
  }

  Widget _buildIndicator();
}

class LinearProgressIndicator extends ProgressIndicator {
  LinearProgressIndicator({
    Key key,
    double value,
    double bufferValue
  }) : super(key: key, value: value, bufferValue: bufferValue);

  void _paint(sky.Canvas canvas, Size size) {
    Paint paint = new Paint()
      ..color = _backgroundColor
      ..setStyle(sky.PaintingStyle.fill);
    canvas.drawRect(Point.origin & size, paint);

    paint.color = _valueColor;
    if (value != null) {
      double width = value.clamp(0.0, 1.0) * size.width;
      canvas.drawRect(Point.origin & new Size(width, size.height), paint);
    } else {
87
      double startX = size.width * (1.5 * _performanceValue - 0.5);
88 89 90 91 92 93 94 95 96
      double endX = startX + 0.5 * size.width;
      double x = startX.clamp(0.0, size.width);
      double width = endX.clamp(0.0, size.width) - x;
      canvas.drawRect(new Point(x, 0.0) & new Size(width, size.height), paint);
    }
  }

  Widget _buildIndicator() {
    return new Container(
97
      child: new CustomPaint(callback: _paint, token: _customPaintToken),
98 99 100 101 102 103 104 105 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
      constraints: new BoxConstraints.tightFor(
        width: double.INFINITY,
        height: _kLinearProgressIndicatorHeight
      )
    );
  }
}

class CircularProgressIndicator extends ProgressIndicator {
  static const _kTwoPI = math.PI * 2.0;
  static const _kEpsilon = .0000001;
  // Canavs.drawArc(r, 0, 2*PI) doesn't draw anything, so just get close.
  static const _kSweep = _kTwoPI - _kEpsilon;
  static const _kStartAngle = -math.PI / 2.0;

  CircularProgressIndicator({
    Key key,
    double value,
    double bufferValue
  }) : super(key: key, value: value, bufferValue: bufferValue);

  void _paint(sky.Canvas canvas, Size size) {
    Paint paint = new Paint()
      ..color = _valueColor
      ..strokeWidth = _kCircularProgressIndicatorStrokeWidth
      ..setStyle(sky.PaintingStyle.stroke);

    if (value != null) {
      double angle = value.clamp(0.0, 1.0) * _kSweep;
      sky.Path path = new sky.Path()
        ..arcTo(Point.origin & size, _kStartAngle, angle, false);
      canvas.drawPath(path, paint);
    } else {
131
      double startAngle = _kTwoPI * (1.75 * _performanceValue - 0.75);
132 133 134 135 136 137 138 139 140 141 142
      double endAngle = startAngle + _kTwoPI * 0.75;
      double arcAngle = startAngle.clamp(0.0, _kTwoPI);
      double arcSweep = endAngle.clamp(0.0, _kTwoPI) - arcAngle;
      sky.Path path = new sky.Path()
        ..arcTo(Point.origin & size, _kStartAngle + arcAngle, arcSweep, false);
      canvas.drawPath(path, paint);
    }
  }

  Widget _buildIndicator() {
    return new Container(
143
      child: new CustomPaint(callback: _paint, token: _customPaintToken),
144 145 146 147 148 149 150
      constraints: new BoxConstraints(
        minWidth: _kMinCircularProgressIndicatorSize,
        minHeight: _kMinCircularProgressIndicatorSize
      )
    );
  }
}