blob: 3b093b0ecbcfe85ee4bf931540d250a2da0ecd1d [file]
// 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 'package:flutter/foundation.dart';
import 'basic.dart';
import 'framework.dart';
const double _kOffset = 40.0; // distance to bottom of banner, at a 45 degree angle inwards
const double _kHeight = 12.0; // height of banner
const double _kBottomOffset = _kOffset + 0.707 * _kHeight; // offset plus sqrt(2)/2 * banner height
final Rect _kRect = new Rect.fromLTWH(-_kOffset, _kOffset - _kHeight, _kOffset * 2.0, _kHeight);
const Color _kColor = const Color(0xA0B71C1C);
const TextStyle _kTextStyle = const TextStyle(
color: const Color(0xFFFFFFFF),
fontSize: _kHeight * 0.85,
fontWeight: FontWeight.w900,
height: 1.0
);
/// Where to show a [Banner].
enum BannerLocation {
/// Show the banner in the top right corner.
topRight,
/// Show the banner in the top left corner.
topLeft,
/// Show the banner in the bottom right corner.
bottomRight,
/// Show the banner in the bottom left corner.
bottomLeft,
}
/// Paints a [Banner].
class BannerPainter extends CustomPainter {
/// Creates a banner painter.
///
/// The [message] and [location] arguments must not be null.
BannerPainter({
@required this.message,
@required this.location,
this.color: _kColor,
this.textStyle: _kTextStyle,
}) {
assert(message != null);
assert(location != null);
assert(color != null);
assert(textStyle != null);
}
/// The message to show in the banner.
final String message;
/// Where to show the banner (e.g., the upper right corder).
final BannerLocation location;
/// The color to paint behind the [message].
///
/// Defaults to a dark red.
final Color color;
/// The text style to use for the [message].
///
/// Defaults to bold, white text.
final TextStyle textStyle;
bool _prepared = false;
TextPainter _textPainter;
Paint _paintShadow;
Paint _paintBanner;
void _prepare() {
_paintShadow = new Paint()
..color = const Color(0x7F000000)
..maskFilter = new MaskFilter.blur(BlurStyle.normal, 4.0);
_paintBanner = new Paint()
..color = color;
_textPainter = new TextPainter(
text: new TextSpan(style: textStyle, text: message),
textAlign: TextAlign.center,
);
_prepared = true;
}
@override
void paint(Canvas canvas, Size size) {
if (!_prepared)
_prepare();
canvas
..translate(_translationX(size.width), _translationY(size.height))
..rotate(_rotation)
..drawRect(_kRect, _paintShadow)
..drawRect(_kRect, _paintBanner);
final double width = _kOffset * 2.0;
_textPainter.layout(minWidth: width, maxWidth: width);
_textPainter.paint(canvas, _kRect.topLeft.toOffset() + new Offset(0.0, (_kRect.height - _textPainter.height) / 2.0));
}
@override
bool shouldRepaint(BannerPainter oldPainter) {
return message != oldPainter.message
|| location != oldPainter.location
|| color != oldPainter.color
|| textStyle != oldPainter.textStyle;
}
@override
bool hitTest(Point position) => false;
double _translationX(double width) {
assert(location != null);
switch (location) {
case BannerLocation.bottomRight:
return width - _kBottomOffset;
case BannerLocation.topRight:
return width;
case BannerLocation.bottomLeft:
return _kBottomOffset;
case BannerLocation.topLeft:
return 0.0;
}
return null;
}
double _translationY(double height) {
assert(location != null);
switch (location) {
case BannerLocation.bottomRight:
case BannerLocation.bottomLeft:
return height - _kBottomOffset;
case BannerLocation.topRight:
case BannerLocation.topLeft:
return 0.0;
}
return null;
}
double get _rotation {
assert(location != null);
switch (location) {
case BannerLocation.bottomLeft:
case BannerLocation.topRight:
return math.PI / 4.0;
case BannerLocation.bottomRight:
case BannerLocation.topLeft:
return -math.PI / 4.0;
}
return null;
}
}
/// Displays a diagonal message above the corner of another widget.
///
/// Useful for showing the execution mode of an app (e.g., that asserts are
/// enabled.)
///
/// See also:
///
/// * [CheckedModeBanner].
class Banner extends StatelessWidget {
/// Creates a banner.
///
/// The [message] and [location] arguments must not be null.
const Banner({
Key key,
this.child,
@required this.message,
@required this.location,
this.color: _kColor,
this.textStyle: _kTextStyle,
}) : assert(message != null),
assert(location != null),
assert(color != null),
assert(textStyle != null),
super(key: key);
/// The widget to show behind the banner.
final Widget child;
/// The message to show in the banner.
final String message;
/// Where to show the banner (e.g., the upper right corder).
final BannerLocation location;
/// The color of the banner.
final Color color;
/// The style of the text shown on the banner.
final TextStyle textStyle;
@override
Widget build(BuildContext context) {
return new CustomPaint(
foregroundPainter: new BannerPainter(
message: message,
location: location,
color: color,
textStyle: textStyle,
),
child: child,
);
}
}
/// Displays a [Banner] saying "SLOW MODE" when running in checked mode.
/// [MaterialApp] builds one of these by default.
/// Does nothing in release mode.
class CheckedModeBanner extends StatelessWidget {
/// Creates a checked mode banner.
const CheckedModeBanner({
Key key,
@required this.child
}) : super(key: key);
/// The widget to show behind the banner.
final Widget child;
@override
Widget build(BuildContext context) {
Widget result = child;
assert(() {
result = new Banner(
child: result,
message: 'SLOW MODE',
location: BannerLocation.topRight);
return true;
});
return result;
}
}