Commit 955b3e21 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

A Flutter logo widget. (#5382)

Instead of a PNG, the Flutter gallery widget is now drawn in code.

There's now a FlutterLogoDecoration class that paints the flutter logo
anywhere you can use a Decoration (e.g. AnimatedContainer).

There's now a FlutterLogo class that honors the IconTheme.

The About dialog box API now takes a Widget for the applicationIcon,
instead of an ImageProvider. It uses IconTheme to make the icon the
right size instead of using an Image widget.

Add padding, duration, and curve properties to the DrawerHeader.
Make the child of a DrawerHeader optional.

Clean up UserAccuntsDrawerHeader a bit.

Add some useful properties and methods to EdgeInsets.

Add some debug logic to RenderDecoratedBox to catch unpaired
save/restore calls when possible.

Make GestureDetector fill its parent if it has no children. Fixes
https://github.com/flutter/flutter/issues/5380
parent e2d0917e
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
...@@ -16,6 +18,69 @@ class LinkTextSpan extends TextSpan { ...@@ -16,6 +18,69 @@ class LinkTextSpan extends TextSpan {
); );
} }
class GalleryDrawerHeader extends StatefulWidget {
const GalleryDrawerHeader({ Key key }) : super(key: key);
@override
_GalleryDrawerHeaderState createState() => new _GalleryDrawerHeaderState();
}
class _GalleryDrawerHeaderState extends State<GalleryDrawerHeader> {
bool _logoHasName = true;
bool _logoHorizontal = true;
Map<int, Color> _swatch = Colors.blue;
@override
Widget build(BuildContext context) {
final double systemTopPadding = MediaQuery.of(context).padding.top;
return new DrawerHeader(
decoration: new FlutterLogoDecoration(
margin: new EdgeInsets.fromLTRB(12.0, 12.0 + systemTopPadding, 12.0, 12.0),
style: _logoHasName ? _logoHorizontal ? FlutterLogoStyle.horizontal
: FlutterLogoStyle.stacked
: FlutterLogoStyle.markOnly,
swatch: _swatch,
),
duration: const Duration(milliseconds: 750),
child: new GestureDetector(
onLongPress: () {
setState(() {
_logoHorizontal = !_logoHorizontal;
if (!_logoHasName)
_logoHasName = true;
});
},
onTap: () {
setState(() {
_logoHasName = !_logoHasName;
});
},
onDoubleTap: () {
setState(() {
final List<Map<int, Color>> options = <Map<int, Color>>[];
if (_swatch != Colors.blue)
options.addAll(<Map<int, Color>>[Colors.blue, Colors.blue, Colors.blue, Colors.blue, Colors.blue, Colors.blue, Colors.blue]);
if (_swatch != Colors.amber)
options.addAll(<Map<int, Color>>[Colors.amber, Colors.amber, Colors.amber]);
if (_swatch != Colors.red)
options.addAll(<Map<int, Color>>[Colors.red, Colors.red, Colors.red]);
if (_swatch != Colors.indigo)
options.addAll(<Map<int, Color>>[Colors.indigo, Colors.indigo, Colors.indigo]);
if (_swatch != Colors.pink)
options.addAll(<Map<int, Color>>[Colors.pink]);
if (_swatch != Colors.purple)
options.addAll(<Map<int, Color>>[Colors.purple]);
if (_swatch != Colors.cyan)
options.addAll(<Map<int, Color>>[Colors.cyan]);
_swatch = options[new math.Random().nextInt(options.length)];
});
}
)
);
}
}
class GalleryDrawer extends StatelessWidget { class GalleryDrawer extends StatelessWidget {
GalleryDrawer({ GalleryDrawer({
Key key, Key key,
...@@ -41,24 +106,14 @@ class GalleryDrawer extends StatelessWidget { ...@@ -41,24 +106,14 @@ class GalleryDrawer extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
ThemeData themeData = Theme.of(context); final ThemeData themeData = Theme.of(context);
TextStyle aboutTextStyle = themeData.textTheme.body2; final TextStyle aboutTextStyle = themeData.textTheme.body2;
TextStyle aboutLinkStyle = themeData.textTheme.body2.copyWith(color: themeData.accentColor); final TextStyle aboutLinkStyle = themeData.textTheme.body2.copyWith(color: themeData.accentColor);
return new Drawer( return new Drawer(
child: new Block( child: new Block(
children: <Widget>[ children: <Widget>[
new DrawerHeader( new GalleryDrawerHeader(),
child: new Center(
child: new Padding(
padding: const EdgeInsets.all(16.0),
child: new Image.asset(
'packages/flutter_gallery_assets/drawer_logo.png',
fit: ImageFit.contain
)
)
)
),
new DrawerItem( new DrawerItem(
icon: new Icon(Icons.brightness_5), icon: new Icon(Icons.brightness_5),
onPressed: () { onThemeChanged(true); }, onPressed: () { onThemeChanged(true); },
...@@ -119,8 +174,9 @@ class GalleryDrawer extends StatelessWidget { ...@@ -119,8 +174,9 @@ class GalleryDrawer extends StatelessWidget {
) )
), ),
new AboutDrawerItem( new AboutDrawerItem(
icon: new FlutterLogo(),
applicationVersion: '2016 Q3 Preview', applicationVersion: '2016 Q3 Preview',
applicationIcon: new AssetImage('packages/flutter_gallery_assets/about_logo.png'), applicationIcon: new FlutterLogo(),
applicationLegalese: '© 2016 The Chromium Authors', applicationLegalese: '© 2016 The Chromium Authors',
aboutBoxChildren: <Widget>[ aboutBoxChildren: <Widget>[
new Padding( new Padding(
......
...@@ -37,6 +37,7 @@ export 'src/material/drop_down.dart'; ...@@ -37,6 +37,7 @@ export 'src/material/drop_down.dart';
export 'src/material/flat_button.dart'; export 'src/material/flat_button.dart';
export 'src/material/flexible_space_bar.dart'; export 'src/material/flexible_space_bar.dart';
export 'src/material/floating_action_button.dart'; export 'src/material/floating_action_button.dart';
export 'src/material/flutter_logo.dart';
export 'src/material/grid_tile.dart'; export 'src/material/grid_tile.dart';
export 'src/material/grid_tile_bar.dart'; export 'src/material/grid_tile_bar.dart';
export 'src/material/icon.dart'; export 'src/material/icon.dart';
......
...@@ -24,6 +24,7 @@ export 'src/painting/decoration.dart'; ...@@ -24,6 +24,7 @@ export 'src/painting/decoration.dart';
export 'src/painting/image_fit.dart'; export 'src/painting/image_fit.dart';
export 'src/painting/edge_insets.dart'; export 'src/painting/edge_insets.dart';
export 'src/painting/fractional_offset.dart'; export 'src/painting/fractional_offset.dart';
export 'src/painting/flutter_logo.dart';
export 'src/painting/text_editing.dart'; export 'src/painting/text_editing.dart';
export 'src/painting/text_painter.dart'; export 'src/painting/text_painter.dart';
export 'src/painting/text_span.dart'; export 'src/painting/text_span.dart';
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
...@@ -14,6 +13,8 @@ import 'dialog.dart'; ...@@ -14,6 +13,8 @@ import 'dialog.dart';
import 'drawer_item.dart'; import 'drawer_item.dart';
import 'flat_button.dart'; import 'flat_button.dart';
import 'icon.dart'; import 'icon.dart';
import 'icon_theme.dart';
import 'icon_theme_data.dart';
import 'page.dart'; import 'page.dart';
import 'progress_indicator.dart'; import 'progress_indicator.dart';
import 'scaffold.dart'; import 'scaffold.dart';
...@@ -84,9 +85,12 @@ class AboutDrawerItem extends StatelessWidget { ...@@ -84,9 +85,12 @@ class AboutDrawerItem extends StatelessWidget {
/// ///
/// By default no icon is shown. /// By default no icon is shown.
/// ///
/// Typically this will be an [ImageIcon] widget. It should honor the
/// [IconTheme]'s [IconThemeData.size].
///
/// This is not necessarily the same as the icon shown on the drawer item /// This is not necessarily the same as the icon shown on the drawer item
/// itself, which is controlled by the [icon] property. /// itself, which is controlled by the [icon] property.
final ImageProvider applicationIcon; final Widget applicationIcon;
/// A string to show in small print in the [AboutDialog]. /// A string to show in small print in the [AboutDialog].
/// ///
...@@ -137,7 +141,7 @@ void showAboutDialog({ ...@@ -137,7 +141,7 @@ void showAboutDialog({
@required BuildContext context, @required BuildContext context,
String applicationName, String applicationName,
String applicationVersion, String applicationVersion,
ImageProvider applicationIcon, Widget applicationIcon,
String applicationLegalese, String applicationLegalese,
List<Widget> children List<Widget> children
}) { }) {
...@@ -168,7 +172,7 @@ void showLicensePage({ ...@@ -168,7 +172,7 @@ void showLicensePage({
@required BuildContext context, @required BuildContext context,
String applicationName, String applicationName,
String applicationVersion, String applicationVersion,
ImageProvider applicationIcon, Widget applicationIcon,
String applicationLegalese String applicationLegalese
}) { }) {
// TODO(ianh): remove pop once https://github.com/flutter/flutter/issues/4667 is fixed // TODO(ianh): remove pop once https://github.com/flutter/flutter/issues/4667 is fixed
...@@ -220,7 +224,10 @@ class AboutDialog extends StatelessWidget { ...@@ -220,7 +224,10 @@ class AboutDialog extends StatelessWidget {
/// The icon to show next to the application name. /// The icon to show next to the application name.
/// ///
/// By default no icon is shown. /// By default no icon is shown.
final ImageProvider applicationIcon; ///
/// Typically this will be an [ImageIcon] widget. It should honor the
/// [IconTheme]'s [IconThemeData.size].
final Widget applicationIcon;
/// A string to show in small print. /// A string to show in small print.
/// ///
...@@ -241,15 +248,10 @@ class AboutDialog extends StatelessWidget { ...@@ -241,15 +248,10 @@ class AboutDialog extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final String name = applicationName ?? _defaultApplicationName(context); final String name = applicationName ?? _defaultApplicationName(context);
final String version = applicationVersion ?? _defaultApplicationVersion(context); final String version = applicationVersion ?? _defaultApplicationVersion(context);
final ImageProvider icon = applicationIcon ?? _defaultApplicationIcon(context); final Widget icon = applicationIcon ?? _defaultApplicationIcon(context);
List<Widget> body = <Widget>[]; List<Widget> body = <Widget>[];
if (icon != null) { if (icon != null)
body.add(new Image( body.add(new IconTheme(data: new IconThemeData(size: 48.0), child: icon));
image: icon,
width: 48.0,
height: 48.0
));
}
body.add(new Flexible( body.add(new Flexible(
child: new Padding( child: new Padding(
padding: new EdgeInsets.symmetric(horizontal: 24.0), padding: new EdgeInsets.symmetric(horizontal: 24.0),
...@@ -454,7 +456,7 @@ String _defaultApplicationVersion(BuildContext context) { ...@@ -454,7 +456,7 @@ String _defaultApplicationVersion(BuildContext context) {
return ''; return '';
} }
ImageProvider _defaultApplicationIcon(BuildContext context) { Widget _defaultApplicationIcon(BuildContext context) {
// TODO(ianh): Get this from the embedder somehow. // TODO(ianh): Get this from the embedder somehow.
return null; return null;
} }
...@@ -10,8 +10,8 @@ import 'theme.dart'; ...@@ -10,8 +10,8 @@ import 'theme.dart';
const double _kDrawerHeaderHeight = 160.0 + 1.0; // bottom edge const double _kDrawerHeaderHeight = 160.0 + 1.0; // bottom edge
/// The top-most region of a material design drawer. The header's [child] /// The top-most region of a material design drawer. The header's [child]
/// widget is placed inside of a [Container] whose [decoration] can be passed as /// widget, if any, is placed inside a [Container] whose [decoration] can be
/// an argument. /// passed as an argument, inset by the given [padding].
/// ///
/// Part of the material design [Drawer]. /// Part of the material design [Drawer].
/// ///
...@@ -20,9 +20,10 @@ const double _kDrawerHeaderHeight = 160.0 + 1.0; // bottom edge ...@@ -20,9 +20,10 @@ const double _kDrawerHeaderHeight = 160.0 + 1.0; // bottom edge
/// See also: /// See also:
/// ///
/// * [Drawer] /// * [Drawer]
/// * [UserAccountsDrawerHeader], a variant of [DrawerHeader] that is
/// specialized for showing user accounts.
/// * [DrawerItem] /// * [DrawerItem]
/// * <https://www.google.com/design/spec/patterns/navigation-drawer.html> /// * <https://www.google.com/design/spec/patterns/navigation-drawer.html>
class DrawerHeader extends StatelessWidget { class DrawerHeader extends StatelessWidget {
/// Creates a material design drawer header. /// Creates a material design drawer header.
/// ///
...@@ -30,15 +31,35 @@ class DrawerHeader extends StatelessWidget { ...@@ -30,15 +31,35 @@ class DrawerHeader extends StatelessWidget {
const DrawerHeader({ const DrawerHeader({
Key key, Key key,
this.decoration, this.decoration,
this.padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 8.0),
this.duration: const Duration(milliseconds: 250),
this.curve: Curves.fastOutSlowIn,
this.child this.child
}) : super(key: key); }) : super(key: key);
/// Decoration for the main drawer header [Container]; useful for applying /// Decoration for the main drawer header [Container]; useful for applying
/// backgrounds. /// backgrounds.
final BoxDecoration decoration; ///
/// This decoration will extend under the system status bar.
///
/// If this is changed, it will be animated according to [duration] and [curve].
final Decoration decoration;
/// The padding by which to inset [child].
///
/// The [DrawerHeader] additionally offsets the child by the height of the
/// system status bar.
///
/// If the child is null, the padding has no effect.
final EdgeInsets padding;
/// The duration for animations of the [decoration].
final Duration duration;
/// The curve for animations of the [decoration].
final Curve curve;
/// A widget that extends behind the system status bar and is placed inside a /// A widget to be placed inside the drawer header, inset by the [padding].
/// [Container].
final Widget child; final Widget child;
@override @override
...@@ -56,15 +77,12 @@ class DrawerHeader extends StatelessWidget { ...@@ -56,15 +77,12 @@ class DrawerHeader extends StatelessWidget {
) )
) )
), ),
child: new Container( child: new AnimatedContainer(
padding: new EdgeInsets.only( padding: padding + new EdgeInsets.only(top: statusBarHeight),
top: 16.0 + statusBarHeight,
left: 16.0,
right: 16.0,
bottom: 8.0
),
decoration: decoration, decoration: decoration,
child: new DefaultTextStyle( duration: duration,
curve: curve,
child: child == null ? null : new DefaultTextStyle(
style: Theme.of(context).textTheme.body2, style: Theme.of(context).textTheme.body2,
child: child child: child
) )
......
// Copyright 2016 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 'icon_theme.dart';
import 'icon_theme_data.dart';
import 'colors.dart';
/// The Flutter logo, in widget form. This widget respects the [IconTheme].
///
/// See also:
///
/// * [IconTheme], which provides ambient configuration for icons.
/// * [Icon], for showing icons the Material design icon library.
/// * [ImageIcon], for showing icons from [AssetImage]s or other [ImageProvider]s.
class FlutterLogo extends StatelessWidget {
/// Creates a widget that paints the Flutter logo.
///
/// The [size] and [color] default to the value given by the current [IconTheme].
const FlutterLogo({
Key key,
this.size,
this.swatch: Colors.blue,
this.style: FlutterLogoStyle.markOnly,
this.duration: const Duration(milliseconds: 750),
}) : super(key: key);
/// The size of the logo in logical pixels.
///
/// The logo will be fit into a square this size.
///
/// Defaults to the current [IconTheme] size, if any. If there is no
/// [IconTheme], or it does not specify an explicit size, then it defaults to
/// 24.0.
final double size;
/// The colors to use to paint the logo. This map should contain at least two
/// values, one for 400 and one for 900.
///
/// If possible, the default should be used. It corresponds to the
/// [Colors.blue] swatch.
///
/// If for some reason that color scheme is impractical, the [Colors.amber],
/// [Colors.red], or [Colors.indigo] swatches can be used. These are Flutter's
/// secondary colors.
///
/// In extreme cases where none of those four color schemes will work,
/// [Colors.pink], [Colors.purple], or [Colors.cyan] swatches can be used.
/// These are Flutter's tertiary colors.
final Map<int, Color> swatch;
/// Whether and where to draw the "Flutter" text. By default, only the logo
/// itself is drawn.
final FlutterLogoStyle style;
/// The length of time for the animation if the [style] or [swatch] properties
/// are changed.
final Duration duration;
@override
Widget build(BuildContext context) {
final IconThemeData iconTheme = IconTheme.of(context).fallback();
final double iconSize = size ?? iconTheme.size;
return new AnimatedContainer(
width: iconSize,
height: iconSize,
duration: duration,
decoration: new FlutterLogoDecoration(
swatch: swatch,
style: style,
),
);
}
}
...@@ -22,9 +22,8 @@ import 'debug.dart'; ...@@ -22,9 +22,8 @@ import 'debug.dart';
/// See also: /// See also:
/// ///
/// * [Drawer] /// * [Drawer]
/// * [DrawerItem] /// * [DrawerHeader], for a drawer header that doesn't show user acounts
/// * <https://www.google.com/design/spec/patterns/navigation-drawer.html> /// * <https://www.google.com/design/spec/patterns/navigation-drawer.html>
class UserAccountsDrawerHeader extends StatefulWidget { class UserAccountsDrawerHeader extends StatefulWidget {
/// Creates a material design drawer header. /// Creates a material design drawer header.
/// ///
...@@ -43,41 +42,37 @@ class UserAccountsDrawerHeader extends StatefulWidget { ...@@ -43,41 +42,37 @@ class UserAccountsDrawerHeader extends StatefulWidget {
/// section is pressed. /// section is pressed.
final VoidCallback onDetailsPressed; final VoidCallback onDetailsPressed;
/// Decoration for the main drawer header container useful for applying /// The background to show in the drawer header.
/// backgrounds. final Decoration decoration;
final BoxDecoration decoration;
/// A widget placed in the upper-left corner representing the current /// A widget placed in the upper-left corner representing the current
/// account picture. Normally a [CircleAvatar]. /// account picture. Normally a [CircleAvatar].
final Widget currentAccountPicture; final Widget currentAccountPicture;
/// A list of widgets that represent the user's accounts. Up to three of them /// A list of widgets that represent the user's accounts. Up to three of will
/// are arranged in a row in the header's upper-right corner. Normally a list /// be arranged in a row in the header's upper-right corner. Normally a list
/// of [CircleAvatar] widgets. /// of [CircleAvatar] widgets.
final List<Widget> otherAccountsPictures; final List<Widget> otherAccountsPictures;
/// A widget placed on the top row of the account details representing /// A widget placed on the top row of the account details representing the
/// account name. /// account's name.
final Widget accountName; final Widget accountName;
/// A widget placed on the bottom row of the account details representing /// A widget placed on the bottom row of the account details representing the
/// account email. /// account's e-mail address.
final Widget accountEmail; final Widget accountEmail;
@override @override
_UserAccountsDrawerHeaderState createState() => _UserAccountsDrawerHeaderState createState() => new _UserAccountsDrawerHeaderState();
new _UserAccountsDrawerHeaderState();
} }
class _UserAccountsDrawerHeaderState extends State<UserAccountsDrawerHeader> { class _UserAccountsDrawerHeaderState extends State<UserAccountsDrawerHeader> {
/// Saves whether the account dropdown is open or not. bool _isOpen = false;
bool isOpen = false;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context)); assert(debugCheckHasMaterial(context));
final List<Widget> otherAccountsPictures = config.otherAccountsPictures ?? final List<Widget> otherAccountsPictures = config.otherAccountsPictures ?? <Widget>[];
<Widget>[];
return new DrawerHeader( return new DrawerHeader(
decoration: config.decoration, decoration: config.decoration,
child: new Column( child: new Column(
...@@ -117,7 +112,7 @@ class _UserAccountsDrawerHeaderState extends State<UserAccountsDrawerHeader> { ...@@ -117,7 +112,7 @@ class _UserAccountsDrawerHeaderState extends State<UserAccountsDrawerHeader> {
child: new InkWell( child: new InkWell(
onTap: () { onTap: () {
setState(() { setState(() {
isOpen = !isOpen; _isOpen = !_isOpen;
}); });
if (config.onDetailsPressed != null) if (config.onDetailsPressed != null)
config.onDetailsPressed(); config.onDetailsPressed();
...@@ -146,8 +141,7 @@ class _UserAccountsDrawerHeaderState extends State<UserAccountsDrawerHeader> { ...@@ -146,8 +141,7 @@ class _UserAccountsDrawerHeaderState extends State<UserAccountsDrawerHeader> {
child: new Align( child: new Align(
alignment: FractionalOffset.centerRight, alignment: FractionalOffset.centerRight,
child: new Icon( child: new Icon(
isOpen ? Icons.arrow_drop_up : _isOpen ? Icons.arrow_drop_up : Icons.arrow_drop_down,
Icons.arrow_drop_down,
color: Colors.white color: Colors.white
) )
) )
......
...@@ -114,6 +114,10 @@ abstract class BoxPainter { ...@@ -114,6 +114,10 @@ abstract class BoxPainter {
/// However, when its size changes, it could continue using those /// However, when its size changes, it could continue using those
/// same instances, since the previous resources would no longer be /// same instances, since the previous resources would no longer be
/// relevant and thus losing them would not be an issue. /// relevant and thus losing them would not be an issue.
///
/// Implementations should paint their decorations on the canvas in a
/// rectangle whose top left corner is at the given `offset` and whose size is
/// given by `configuration.size`.
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration); void paint(Canvas canvas, Offset offset, ImageConfiguration configuration);
/// Callback that is invoked if an asynchronously-loading resource used by the /// Callback that is invoked if an asynchronously-loading resource used by the
......
...@@ -56,6 +56,22 @@ class EdgeInsets { ...@@ -56,6 +56,22 @@ class EdgeInsets {
/// The offset from the bottom. /// The offset from the bottom.
final double bottom; final double bottom;
/// An Offset describing the vector from the top left of a rectangle to the
/// top left of that rectangle inset by this object.
Offset get topLeft => new Offset(left, top);
/// An Offset describing the vector from the top right of a rectangle to the
/// top right of that rectangle inset by this object.
Offset get topRight => new Offset(-right, top);
/// An Offset describing the vector from the bottom left of a rectangle to the
/// bottom left of that rectangle inset by this object.
Offset get bottomLeft => new Offset(left, -bottom);
/// An Offset describing the vector from the bottom right of a rectangle to the
/// bottom right of that rectangle inset by this object.
Offset get bottomRight => new Offset(-right, -bottom);
/// Whether every dimension is non-negative. /// Whether every dimension is non-negative.
bool get isNonNegative => left >= 0.0 && top >= 0.0 && right >= 0.0 && bottom >= 0.0; bool get isNonNegative => left >= 0.0 && top >= 0.0 && right >= 0.0 && bottom >= 0.0;
...@@ -88,10 +104,57 @@ class EdgeInsets { ...@@ -88,10 +104,57 @@ class EdgeInsets {
/// rect is moved left by [left], the top edge of the rect is moved up by /// rect is moved left by [left], the top edge of the rect is moved up by
/// [top], the right edge of the rect is moved right by [right], and the /// [top], the right edge of the rect is moved right by [right], and the
/// bottom edge of the rect is moved down by [bottom]. /// bottom edge of the rect is moved down by [bottom].
///
/// See also:
///
/// * [inflateSize], to inflate a [Size] rather than a [Rect].
/// * [deflateRect], to deflate a [Rect] rather than inflating it.
Rect inflateRect(Rect rect) { Rect inflateRect(Rect rect) {
return new Rect.fromLTRB(rect.left - left, rect.top - top, rect.right + right, rect.bottom + bottom); return new Rect.fromLTRB(rect.left - left, rect.top - top, rect.right + right, rect.bottom + bottom);
} }
/// Returns a new rect that is smaller than the given rect in each direction by
/// the amount of inset in each direction. Specifically, the left edge of the
/// rect is moved right by [left], the top edge of the rect is moved down by
/// [top], the right edge of the rect is moved left by [right], and the
/// bottom edge of the rect is moved up by [bottom].
///
/// If the argument's [Rect.size] is smaller than [collapsedSize], then the
/// resulting rectangle will have negative dimensions.
///
/// See also:
///
/// * [deflateSize], to deflate a [Size] rather than a [Rect].
/// * [inflateRect], to inflate a [Rect] rather than deflating it.
Rect deflateRect(Rect rect) {
return new Rect.fromLTRB(rect.left + left, rect.top + top, rect.right - right, rect.bottom - bottom);
}
/// Returns a new size that is bigger than the given size by the amount of
/// inset in the horizontal and vertical directions.
///
/// See also:
///
/// * [inflateRect], to inflate a [Rect] rather than a [Size].
/// * [deflateSize], to deflate a [Size] rather than inflating it.
Size inflateSize(Size size) {
return new Size(size.width + horizontal, size.height + vertical);
}
/// Returns a new size that is smaller than the given size by the amount of
/// inset in the horizontal and vertical directions.
///
/// If the argument is smaller than [collapsedSize], then the resulting size
/// will have negative dimensions.
///
/// See also:
///
/// * [deflateRect], to deflate a [Rect] rather than a [Size].
/// * [inflateSize], to inflate a [Size] rather than deflating it.
Size deflateSize(Size size) {
return new Size(size.width - horizontal, size.height - vertical);
}
/// Returns the difference between two EdgeInsets. /// Returns the difference between two EdgeInsets.
EdgeInsets operator -(EdgeInsets other) { EdgeInsets operator -(EdgeInsets other) {
return new EdgeInsets.fromLTRB( return new EdgeInsets.fromLTRB(
......
This diff is collapsed.
...@@ -1168,7 +1168,27 @@ class RenderDecoratedBox extends RenderProxyBox { ...@@ -1168,7 +1168,27 @@ class RenderDecoratedBox extends RenderProxyBox {
_painter ??= _decoration.createBoxPainter(markNeedsPaint); _painter ??= _decoration.createBoxPainter(markNeedsPaint);
final ImageConfiguration filledConfiguration = configuration.copyWith(size: size); final ImageConfiguration filledConfiguration = configuration.copyWith(size: size);
if (position == DecorationPosition.background) { if (position == DecorationPosition.background) {
int debugSaveCount;
assert(() {
debugSaveCount = context.canvas.getSaveCount();
return true;
});
_painter.paint(context.canvas, offset, filledConfiguration); _painter.paint(context.canvas, offset, filledConfiguration);
assert(() {
if (debugSaveCount != context.canvas.getSaveCount()) {
throw new FlutterError(
'${_decoration.runtimeType} painter had mismatching save and restore calls.\n'
'Before painting the decoration, the canvas save count was $debugSaveCount. '
'After painting it, the canvas save count was ${context.canvas.getSaveCount()}. '
'Every call to save() or saveLayer() must be matched by a call to restore().\n'
'The decoration was:\n'
'${decoration.toString(" ")}\n'
'The painter was:\n'
' $_painter'
);
}
return true;
});
if (decoration.isComplex) if (decoration.isComplex)
context.setIsComplexHint(); context.setIsComplexHint();
} }
......
...@@ -21,7 +21,7 @@ ImageConfiguration createLocalImageConfiguration(BuildContext context, { Size si ...@@ -21,7 +21,7 @@ ImageConfiguration createLocalImageConfiguration(BuildContext context, { Size si
return new ImageConfiguration( return new ImageConfiguration(
bundle: DefaultAssetBundle.of(context), bundle: DefaultAssetBundle.of(context),
devicePixelRatio: MediaQuery.of(context).devicePixelRatio, devicePixelRatio: MediaQuery.of(context).devicePixelRatio,
// TODO(ianh): provide the locale, // TODO(ianh): provide the locale
size: size, size: size,
platform: Platform.operatingSystem platform: Platform.operatingSystem
); );
......
...@@ -215,6 +215,8 @@ abstract class AnimatedWidgetBaseState<T extends ImplicitlyAnimatedWidget> exten ...@@ -215,6 +215,8 @@ abstract class AnimatedWidgetBaseState<T extends ImplicitlyAnimatedWidget> exten
/// different parameters to [Container]. For more complex animations, you'll /// different parameters to [Container]. For more complex animations, you'll
/// likely want to use a subclass of [Transition] or use an /// likely want to use a subclass of [Transition] or use an
/// [AnimationController] yourself. /// [AnimationController] yourself.
///
/// Properties that are null are not animated.
class AnimatedContainer extends ImplicitlyAnimatedWidget { class AnimatedContainer extends ImplicitlyAnimatedWidget {
/// Creates a container that animates its parameters implicitly. /// Creates a container that animates its parameters implicitly.
/// ///
......
...@@ -7,7 +7,7 @@ import 'package:flutter/rendering.dart'; ...@@ -7,7 +7,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
testWidgets('UserAccuntsDrawerHeader test', (WidgetTester tester) async { testWidgets('UserAccountsDrawerHeader test', (WidgetTester tester) async {
final Key avatarA = new Key('A'); final Key avatarA = new Key('A');
final Key avatarC = new Key('C'); final Key avatarC = new Key('C');
final Key avatarD = new Key('D'); final Key avatarD = new Key('D');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment