circle_avatar.dart 6.76 KB
Newer Older
Adam Barth's avatar
Adam Barth committed
1 2 3 4 5 6 7 8
// 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 'package:flutter/widgets.dart';

import 'constants.dart';
import 'theme.dart';
9
import 'theme_data.dart';
Adam Barth's avatar
Adam Barth committed
10

11 12 13
// Examples can assume:
// String userAvatarUrl;

Hixie's avatar
Hixie committed
14 15
/// A circle that represents a user.
///
16
/// Typically used with a user's profile image, or, in the absence of
Hixie's avatar
Hixie committed
17 18 19
/// such an image, the user's initials. A given user's initials should
/// always be paired with the same background color, for consistency.
///
Ian Hickson's avatar
Ian Hickson committed
20 21
/// ## Sample code
///
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
/// If the avatar is to have an image, the image should be specified in the
/// [backgroundImage] property:
///
/// ```dart
/// new CircleAvatar(
///   backgroundImage: new NetworkImage(userAvatarUrl),
/// )
/// ```
///
/// The image will be cropped to have a circle shape.
///
/// If the avatar is to just have the user's initials, they are typically
/// provided using a [Text] widget as the [child] and a [backgroundColor]:
///
/// ```dart
/// new CircleAvatar(
38
///   backgroundColor: Colors.brown.shade800,
39
///   child: new Text('AH'),
Ian Hickson's avatar
Ian Hickson committed
40
/// )
41 42
/// ```
///
43
/// See also:
44
///
45
///  * [Chip], for representing users or concepts in long form.
46 47
///  * [ListTile], which can combine an icon (such as a [CircleAvatar]) with
///    some text for a fixed height list entry.
48
///  * <https://material.google.com/components/chips.html#chips-contact-chips>
49
class CircleAvatar extends StatelessWidget {
50
  /// Creates a circle that represents a user.
51
  const CircleAvatar({
Adam Barth's avatar
Adam Barth committed
52
    Key key,
Hans Muller's avatar
Hans Muller committed
53
    this.child,
Adam Barth's avatar
Adam Barth committed
54
    this.backgroundColor,
55
    this.backgroundImage,
56
    this.foregroundColor,
57 58 59 60 61
    this.radius,
    this.minRadius,
    this.maxRadius,
  })  : assert(radius == null || (minRadius == null && maxRadius == null)),
        super(key: key);
Adam Barth's avatar
Adam Barth committed
62

63
  /// The widget below this widget in the tree.
64 65 66
  ///
  /// Typically a [Text] widget. If the [CircleAvatar] is to have an image, use
  /// [backgroundImage] instead.
Hans Muller's avatar
Hans Muller committed
67
  final Widget child;
Hixie's avatar
Hixie committed
68 69 70

  /// The color with which to fill the circle. Changing the background
  /// color will cause the avatar to animate to the new color.
71
  ///
72 73 74
  /// If a [backgroundColor] is not specified, the theme's
  /// [ThemeData.primaryColorLight] is used with dark foreground colors, and
  /// [ThemeData.primaryColorDark] with light foreground colors.
Adam Barth's avatar
Adam Barth committed
75
  final Color backgroundColor;
Hixie's avatar
Hixie committed
76

77 78
  /// The default text color for text in the circle.
  ///
79 80 81 82 83
  /// Defaults to the primary text theme color if no [backgroundColor] is
  /// specified.
  ///
  /// Defaults to [ThemeData.primaryColorLight] for dark background colors, and
  /// [ThemeData.primaryColorDark] for light background colors.
84 85
  final Color foregroundColor;

86 87
  /// The background image of the circle. Changing the background
  /// image will cause the avatar to animate to the new image.
88 89
  ///
  /// If the [CircleAvatar] is to have the user's initials, use [child] instead.
90 91
  final ImageProvider backgroundImage;

Hixie's avatar
Hixie committed
92 93
  /// The size of the avatar. Changing the radius will cause the
  /// avatar to animate to the new size.
94
  ///
95 96 97 98
  /// If [radius] is specified, then neither [minRadius] nor [maxRadius] may be
  /// specified. Specifying [radius] is equivalent to specifying a [minRadius]
  /// and [maxRadius], both with the value of [radius].
  ///
99
  /// Defaults to 20 logical pixels.
Hans Muller's avatar
Hans Muller committed
100
  final double radius;
Adam Barth's avatar
Adam Barth committed
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 131 132 133 134 135 136 137 138 139 140 141 142 143 144
  /// The minimum size of the avatar.
  ///
  /// Changing the minRadius may cause the avatar to animate to the new size, if
  /// constraints allow.
  ///
  /// If minRadius is specified, then [radius] must not also be specified.
  ///
  /// Defaults to zero.
  final double minRadius;

  /// The maximum size of the avatar.
  ///
  /// Changing the maxRadius will cause the avatar to animate to the new size,
  /// if constraints allow.
  ///
  /// If maxRadius is specified, then [radius] must not also be specified.
  ///
  /// Defaults to [double.infinity].
  final double maxRadius;

  // The default radius if nothing is specified.
  static const double _defaultRadius = 20.0;

  // The default min if only the max is specified.
  static const double _defaultMinRadius = 0.0;

  // The default max if only the min is specified.
  static const double _defaultMaxRadius = double.infinity;

  double get _minDiameter {
    if (radius == null && minRadius == null && maxRadius == null) {
      return _defaultRadius * 2.0;
    }
    return 2.0 * (radius ?? minRadius ?? _defaultMinRadius);
  }

  double get _maxDiameter {
    if (radius == null && minRadius == null && maxRadius == null) {
      return _defaultRadius * 2.0;
    }
    return 2.0 * (radius ?? maxRadius ?? _defaultMaxRadius);
  }

145
  @override
Adam Barth's avatar
Adam Barth committed
146
  Widget build(BuildContext context) {
147
    assert(debugCheckHasMediaQuery(context));
Hans Muller's avatar
Hans Muller committed
148
    final ThemeData theme = Theme.of(context);
149
    TextStyle textStyle = theme.primaryTextTheme.subhead.copyWith(color: foregroundColor);
150 151 152 153 154 155 156 157 158 159 160
    Color effectiveBackgroundColor = backgroundColor;
    if (effectiveBackgroundColor == null) {
      switch (ThemeData.estimateBrightnessForColor(textStyle.color)) {
        case Brightness.dark:
          effectiveBackgroundColor = theme.primaryColorLight;
          break;
        case Brightness.light:
          effectiveBackgroundColor = theme.primaryColorDark;
          break;
      }
    } else if (foregroundColor == null) {
161 162
      switch (ThemeData.estimateBrightnessForColor(backgroundColor)) {
        case Brightness.dark:
163
          textStyle = textStyle.copyWith(color: theme.primaryColorLight);
164 165
          break;
        case Brightness.light:
166
          textStyle = textStyle.copyWith(color: theme.primaryColorDark);
167 168 169
          break;
      }
    }
170 171
    final double minDiameter = _minDiameter;
    final double maxDiameter = _maxDiameter;
Adam Barth's avatar
Adam Barth committed
172
    return new AnimatedContainer(
173 174 175 176 177 178
      constraints: new BoxConstraints(
        minHeight: minDiameter,
        minWidth: minDiameter,
        maxWidth: maxDiameter,
        maxHeight: maxDiameter,
      ),
Adam Barth's avatar
Adam Barth committed
179 180
      duration: kThemeChangeDuration,
      decoration: new BoxDecoration(
181
        color: effectiveBackgroundColor,
182 183 184
        image: backgroundImage != null
          ? new DecorationImage(image: backgroundImage, fit: BoxFit.cover)
          : null,
185
        shape: BoxShape.circle,
Adam Barth's avatar
Adam Barth committed
186
      ),
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
      child: child == null
          ? null
          : new Center(
              child: new MediaQuery(
                // Need to ignore the ambient textScaleFactor here so that the
                // text doesn't escape the avatar when the textScaleFactor is large.
                data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
                child: new IconTheme(
                  data: theme.iconTheme.copyWith(color: textStyle.color),
                  child: new DefaultTextStyle(
                    style: textStyle,
                    child: child,
                  ),
                ),
              ),
            ),
Adam Barth's avatar
Adam Barth committed
203 204 205
    );
  }
}