22// for details. All rights reserved. Use of this source code is governed by a
33// BSD-style license that can be found in the LICENSE file.
44
5+ import 'dart:typed_data' ;
6+
57import 'package:flutter/foundation.dart' ;
68import 'package:flutter/material.dart' ;
79
@@ -61,8 +63,14 @@ class GoogleUserCircleAvatar extends StatelessWidget {
6163
6264 /// Adds sizing information to [photoUrl] , inserted as the last path segment
6365 /// before the image filename. The format is described in [sizeDirective] .
66+ ///
67+ /// Falls back to the default profile photo if [photoUrl] is [null] .
6468 static String _sizedProfileImageUrl (String photoUrl, double size) {
65- assert (photoUrl != null );
69+ if (photoUrl == null ) {
70+ // If the user has no profile photo and no display name, fall back to
71+ // the default profile photo as a last resort.
72+ return 'https://lh3.googleusercontent.com/a/default-user=s${size .round ()}-c' ;
73+ }
6674 final Uri profileUri = Uri .parse (photoUrl);
6775 final List <String > pathSegments =
6876 new List <String >.from (profileUri.pathSegments);
@@ -80,30 +88,46 @@ class GoogleUserCircleAvatar extends StatelessWidget {
8088 Widget _buildClippedImage (BuildContext context, BoxConstraints constraints) {
8189 assert (constraints.maxWidth == constraints.maxHeight);
8290
83- String photoUrl = identity.photoUrl ?? placeholderPhotoUrl;
84- if (photoUrl == null &&
85- identity.displayName != null &&
86- identity.displayName.startsWith (new RegExp (r'[A-Z][a-z]' ))) {
87- // Display the user's initials rather than a profile photo.
88- return new Text (identity.displayName[0 ].toUpperCase ());
91+ // Placeholder to use when there is no photo URL, and while the photo is
92+ // loading. Uses the first character of the display name (if it has one),
93+ // or the first letter of the email address if it does not.
94+ final List <String > placeholderCharSources = < String > [
95+ identity.displayName,
96+ identity.email,
97+ '-' ,
98+ ];
99+ final String placeholderChar = placeholderCharSources
100+ .firstWhere ((String str) => str != null && str.trimLeft ().isNotEmpty)
101+ .trimLeft ()[0 ]
102+ .toUpperCase ();
103+ final Widget placeholder = new Center (
104+ child: new Text (placeholderChar, textAlign: TextAlign .center),
105+ );
106+
107+ final String photoUrl = identity.photoUrl ?? placeholderPhotoUrl;
108+ if (photoUrl == null ) {
109+ return placeholder;
89110 }
90111
91- // Add a sizing directive to the profile photo URL if we have one .
112+ // Add a sizing directive to the profile photo URL.
92113 final double size =
93114 MediaQuery .of (context).devicePixelRatio * constraints.maxWidth;
94- if (photoUrl != null ) {
95- photoUrl = _sizedProfileImageUrl (photoUrl, size);
96- }
115+ final String sizedPhotoUrl = _sizedProfileImageUrl (photoUrl, size);
97116
98- // If the user has no profile photo and no display name, fall back to
99- // the default profile photo as a last resort.
100- photoUrl ?? =
101- 'https://lh3.googleusercontent.com/a/default-user=s${size .round ()}-c' ;
102-
103- return new ClipOval (
104- child: new Image (
105- image: new NetworkImage (photoUrl),
106- ),
107- );
117+ // Fade the photo in over the top of the placeholder.
118+ return new SizedBox (
119+ width: size,
120+ height: size,
121+ child: new ClipOval (
122+ child: new Stack (fit: StackFit .expand, children: < Widget > [
123+ placeholder,
124+ new FadeInImage .memoryNetwork (
125+ // This creates a transparent placeholder image, so that
126+ // [placeholder] shows through.
127+ placeholder: new Uint8List ((size.round () * size.round ())),
128+ image: sizedPhotoUrl,
129+ )
130+ ]),
131+ ));
108132 }
109133}
0 commit comments