AppBar: fix bugs, add docs, add samples (#10223)
I added some tests for the bug that I fixed. I added docs for IconButton and AppBar. I added some new constructors for FractionalOffset.
This commit is contained in:
parent
04d418beac
commit
59025702db
@ -119,17 +119,55 @@ class _ToolbarContainerLayout extends SingleChildLayoutDelegate {
|
||||
///
|
||||
/// An app bar consists of a toolbar and potentially other widgets, such as a
|
||||
/// [TabBar] and a [FlexibleSpaceBar]. App bars typically expose one or more
|
||||
/// common actions with [IconButton]s which are optionally followed by a
|
||||
/// [PopupMenuButton] for less common operations.
|
||||
/// common [actions] with [IconButton]s which are optionally followed by a
|
||||
/// [PopupMenuButton] for less common operations (sometimes called the "overflow
|
||||
/// menu").
|
||||
///
|
||||
/// App bars are typically used in the [Scaffold.appBar] property, which places
|
||||
/// the app bar as a fixed-height widget at the top of the screen. For a
|
||||
/// scrollable app bar, see [SliverAppBar], which embeds an [AppBar] in a sliver
|
||||
/// for use in a [CustomScrollView].
|
||||
///
|
||||
/// The AppBar displays the toolbar widgets, [leading], [title], and
|
||||
/// [actions], above the [bottom] (if any). If a [flexibleSpace] widget is
|
||||
/// specified then it is stacked behind the toolbar and the bottom widget.
|
||||
/// The AppBar displays the toolbar widgets, [leading], [title], and [actions],
|
||||
/// above the [bottom] (if any). The [bottom] is usually used for a [TabBar]. If
|
||||
/// a [flexibleSpace] widget is specified then it is stacked behind the toolbar
|
||||
/// and the bottom widget. The following diagram shows where each of these slots
|
||||
/// appears in the toolbar when the writing language is left-to-right (e.g.
|
||||
/// English):
|
||||
///
|
||||
/// 
|
||||
///
|
||||
/// If the [leading] widget is omitted, but the [AppBar] is in a [Scaffold] with
|
||||
/// a [Drawer], then a button will be inserted to open the drawer. Otherwise, if
|
||||
/// the nearest [Navigator] has any previous routes, a [BackButton] is inserted
|
||||
/// instead.
|
||||
///
|
||||
/// ## Sample code
|
||||
///
|
||||
/// ```dart
|
||||
/// new AppBar(
|
||||
/// title: new Text('My Fancy Dress'),
|
||||
/// actions: <Widget>[
|
||||
/// new IconButton(
|
||||
/// icon: new Icon(Icons.playlist_play),
|
||||
/// tooltip: 'Air it',
|
||||
/// onPressed: _airDress,
|
||||
/// ),
|
||||
/// new IconButton(
|
||||
/// icon: new Icon(Icons.playlist_add),
|
||||
/// tooltip: 'Restitch it',
|
||||
/// onPressed: _restitchDress,
|
||||
/// ),
|
||||
/// new IconButton(
|
||||
/// icon: new Icon(Icons.playlist_add_check),
|
||||
/// tooltip: 'Repair it',
|
||||
/// onPressed: _repairDress,
|
||||
/// ),
|
||||
/// ],
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
@ -451,11 +489,17 @@ class _AppBarState extends State<AppBar> {
|
||||
);
|
||||
}
|
||||
|
||||
appBar = new Align(
|
||||
alignment: FractionalOffset.topCenter,
|
||||
child: appBar,
|
||||
);
|
||||
|
||||
if (widget.flexibleSpace != null) {
|
||||
appBar = new Stack(
|
||||
fit: StackFit.passthrough,
|
||||
children: <Widget>[
|
||||
widget.flexibleSpace,
|
||||
new Positioned(top: 0.0, left: 0.0, right: 0.0, child: appBar),
|
||||
appBar,
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -463,10 +507,7 @@ class _AppBarState extends State<AppBar> {
|
||||
return new Material(
|
||||
color: widget.backgroundColor ?? themeData.primaryColor,
|
||||
elevation: widget.elevation,
|
||||
child: new Align(
|
||||
alignment: FractionalOffset.topCenter,
|
||||
child: appBar,
|
||||
),
|
||||
child: appBar,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,16 @@ const double _kMinButtonSize = 48.0;
|
||||
/// requirements in the Material Design specification. The [alignment] controls
|
||||
/// how the icon itself is positioned within the hit region.
|
||||
///
|
||||
/// ## Sample code
|
||||
///
|
||||
/// ```dart
|
||||
/// new IconButton(
|
||||
/// icon: new Icon(Icons.volume_up),
|
||||
/// tooltip: 'Increase volume by 10%',
|
||||
/// onPressed: () { setState(() { _volume *= 1.1; }); },
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Icons], a library of predefined icons.
|
||||
|
@ -8,10 +8,14 @@ import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'basic_types.dart';
|
||||
|
||||
/// An offset that's expressed as a fraction of a Size.
|
||||
/// An offset that's expressed as a fraction of a [Size].
|
||||
///
|
||||
/// FractionalOffset(1.0, 0.0) represents the top right of the Size,
|
||||
/// FractionalOffset(0.0, 1.0) represents the bottom left of the Size,
|
||||
/// `FractionalOffset(1.0, 0.0)` represents the top right of the [Size].
|
||||
///
|
||||
/// `FractionalOffset(0.0, 1.0)` represents the bottom left of the [Size].
|
||||
///
|
||||
/// `FractionalOffset(0.5, 2.0)` represents a point half way across the [Size],
|
||||
/// below the bottom of the rectangle by the height of the [Size].
|
||||
@immutable
|
||||
class FractionalOffset {
|
||||
/// Creates a fractional offset.
|
||||
@ -21,16 +25,47 @@ class FractionalOffset {
|
||||
: assert(dx != null),
|
||||
assert(dy != null);
|
||||
|
||||
/// Creates a fractional offset from a specific offset and size.
|
||||
///
|
||||
/// The returned [FractionalOffset] describes the position of the
|
||||
/// [Offset] in the [Size], as a fraction of the [Size].
|
||||
FractionalOffset.fromOffsetAndSize(Offset offset, Size size) :
|
||||
assert(size != null),
|
||||
assert(offset != null),
|
||||
dx = offset.dx / size.width,
|
||||
dy = offset.dy / size.height;
|
||||
|
||||
/// Creates a fractional offset from a specific offset and rectangle.
|
||||
///
|
||||
/// The offset is assumed to be relative to the same origin as the rectangle.
|
||||
///
|
||||
/// If the offset is relative to the top left of the rectangle, use [new
|
||||
/// FractionalOffset.fromOffsetAndSize] instead, passing `rect.size`.
|
||||
///
|
||||
/// The returned [FractionalOffset] describes the position of the
|
||||
/// [Offset] in the [Rect], as a fraction of the [Rect].
|
||||
factory FractionalOffset.fromOffsetAndRect(Offset offset, Rect rect) {
|
||||
return new FractionalOffset.fromOffsetAndSize(
|
||||
offset - rect.topLeft,
|
||||
rect.size,
|
||||
);
|
||||
}
|
||||
|
||||
/// The distance fraction in the horizontal direction.
|
||||
///
|
||||
/// A value of 0.0 corresponds to the leftmost edge. A value of 1.0
|
||||
/// corresponds to the rightmost edge.
|
||||
/// corresponds to the rightmost edge. Values are not limited to that range;
|
||||
/// negative values represent positions to the left of the left edge, and
|
||||
/// values greater than 1.0 represent positions to the right of the right
|
||||
/// edge.
|
||||
final double dx;
|
||||
|
||||
/// The distance fraction in the vertical direction.
|
||||
///
|
||||
/// A value of 0.0 corresponds to the topmost edge. A value of 1.0
|
||||
/// corresponds to the bottommost edge.
|
||||
/// A value of 0.0 corresponds to the topmost edge. A value of 1.0 corresponds
|
||||
/// to the bottommost edge. Values are not limited to that range; negative
|
||||
/// values represent positions above the top, and values greated than 1.0
|
||||
/// represent positions below the bottom.
|
||||
final double dy;
|
||||
|
||||
/// The top left corner.
|
||||
|
@ -752,6 +752,12 @@ class Padding extends SingleChildRenderObjectWidget {
|
||||
void updateRenderObject(BuildContext context, RenderPadding renderObject) {
|
||||
renderObject.padding = padding;
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillDescription(List<String> description) {
|
||||
super.debugFillDescription(description);
|
||||
description.add('padding: $padding');
|
||||
}
|
||||
}
|
||||
|
||||
/// A widget that aligns its child within itself and optionally sizes itself
|
||||
|
@ -779,4 +779,128 @@ void main() {
|
||||
);
|
||||
expect(find.byIcon(Icons.menu), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('AppBar handles loose children 0', (WidgetTester tester) async {
|
||||
final GlobalKey key = new GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
new MaterialApp(
|
||||
home: new Center(
|
||||
child: new AppBar(
|
||||
leading: new Placeholder(key: key),
|
||||
title: const Text('Abc'),
|
||||
actions: <Widget>[
|
||||
const Placeholder(),
|
||||
const Placeholder(),
|
||||
const Placeholder(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(tester.renderObject<RenderBox>(find.byKey(key)).localToGlobal(Offset.zero), const Offset(0.0, 0.0));
|
||||
expect(tester.renderObject<RenderBox>(find.byKey(key)).size, const Size(56.0, 56.0));
|
||||
});
|
||||
|
||||
testWidgets('AppBar handles loose children 1', (WidgetTester tester) async {
|
||||
final GlobalKey key = new GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
new MaterialApp(
|
||||
home: new Center(
|
||||
child: new AppBar(
|
||||
leading: new Placeholder(key: key),
|
||||
title: const Text('Abc'),
|
||||
actions: <Widget>[
|
||||
const Placeholder(),
|
||||
const Placeholder(),
|
||||
const Placeholder(),
|
||||
],
|
||||
flexibleSpace: new DecoratedBox(
|
||||
decoration: new BoxDecoration(
|
||||
gradient: new LinearGradient(
|
||||
begin: const FractionalOffset(0.50, 0.0),
|
||||
end: const FractionalOffset(0.48, 1.0),
|
||||
colors: <Color>[Colors.blue.shade500, Colors.blue.shade800],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(tester.renderObject<RenderBox>(find.byKey(key)).localToGlobal(Offset.zero), const Offset(0.0, 0.0));
|
||||
expect(tester.renderObject<RenderBox>(find.byKey(key)).size, const Size(56.0, 56.0));
|
||||
});
|
||||
|
||||
testWidgets('AppBar handles loose children 2', (WidgetTester tester) async {
|
||||
final GlobalKey key = new GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
new MaterialApp(
|
||||
home: new Center(
|
||||
child: new AppBar(
|
||||
leading: new Placeholder(key: key),
|
||||
title: const Text('Abc'),
|
||||
actions: <Widget>[
|
||||
const Placeholder(),
|
||||
const Placeholder(),
|
||||
const Placeholder(),
|
||||
],
|
||||
flexibleSpace: new DecoratedBox(
|
||||
decoration: new BoxDecoration(
|
||||
gradient: new LinearGradient(
|
||||
begin: const FractionalOffset(0.50, 0.0),
|
||||
end: const FractionalOffset(0.48, 1.0),
|
||||
colors: <Color>[Colors.blue.shade500, Colors.blue.shade800],
|
||||
),
|
||||
),
|
||||
),
|
||||
bottom: new PreferredSize(
|
||||
preferredSize: const Size(0.0, kToolbarHeight),
|
||||
child: new Container(
|
||||
height: 50.0,
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: const Placeholder(
|
||||
strokeWidth: 2.0,
|
||||
color: const Color(0xFFFFFFFF),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(tester.renderObject<RenderBox>(find.byKey(key)).localToGlobal(Offset.zero), const Offset(0.0, 0.0));
|
||||
expect(tester.renderObject<RenderBox>(find.byKey(key)).size, const Size(56.0, 56.0));
|
||||
});
|
||||
|
||||
testWidgets('AppBar handles loose children 3', (WidgetTester tester) async {
|
||||
final GlobalKey key = new GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
new MaterialApp(
|
||||
home: new Center(
|
||||
child: new AppBar(
|
||||
leading: new Placeholder(key: key),
|
||||
title: const Text('Abc'),
|
||||
actions: <Widget>[
|
||||
const Placeholder(),
|
||||
const Placeholder(),
|
||||
const Placeholder(),
|
||||
],
|
||||
bottom: new PreferredSize(
|
||||
preferredSize: const Size(0.0, kToolbarHeight),
|
||||
child: new Container(
|
||||
height: 50.0,
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: const Placeholder(
|
||||
strokeWidth: 2.0,
|
||||
color: const Color(0xFFFFFFFF),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(tester.renderObject<RenderBox>(find.byKey(key)).localToGlobal(Offset.zero), const Offset(0.0, 0.0));
|
||||
expect(tester.renderObject<RenderBox>(find.byKey(key)).size, const Size(56.0, 56.0));
|
||||
});
|
||||
}
|
||||
|
@ -26,4 +26,14 @@ void main() {
|
||||
expect(FractionalOffset.lerp(null, b, 0.25), equals(b * 0.25));
|
||||
expect(FractionalOffset.lerp(a, null, 0.25), equals(a * 0.75));
|
||||
});
|
||||
|
||||
test('FractionalOffset.fromOffsetAndSize()', () {
|
||||
final FractionalOffset a = new FractionalOffset.fromOffsetAndSize(const Offset(100.0, 100.0), const Size(200.0, 400.0));
|
||||
expect(a, const FractionalOffset(0.5, 0.25));
|
||||
});
|
||||
|
||||
test('FractionalOffset.fromOffsetAndRect()', () {
|
||||
final FractionalOffset a = new FractionalOffset.fromOffsetAndRect(const Offset(150.0, 120.0), new Rect.fromLTWH(50.0, 20.0, 200.0, 400.0));
|
||||
expect(a, const FractionalOffset(0.5, 0.25));
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user