Commit 94ed7dce authored by Mehmet Fidanboylu's avatar Mehmet Fidanboylu Committed by GitHub

Support automaticallyImplyLeading param in AppBar (#11264)

* Support automaticallyImplyLeading param in AppBar

* Review fixes

* fix review comments
parent a92a6270
...@@ -84,7 +84,9 @@ class _ToolbarContainerLayout extends SingleChildLayoutDelegate { ...@@ -84,7 +84,9 @@ class _ToolbarContainerLayout extends SingleChildLayoutDelegate {
/// If the [leading] widget is omitted, but the [AppBar] is in a [Scaffold] with /// 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 /// 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 /// the nearest [Navigator] has any previous routes, a [BackButton] is inserted
/// instead. /// instead. This behavior can be turned off by setting the [automaticallyImplyLeading]
/// to false. In that case a null leading widget will result in the middle/title widget
/// stretching to start.
/// ///
/// ## Sample code /// ## Sample code
/// ///
...@@ -126,10 +128,14 @@ class _ToolbarContainerLayout extends SingleChildLayoutDelegate { ...@@ -126,10 +128,14 @@ class _ToolbarContainerLayout extends SingleChildLayoutDelegate {
class AppBar extends StatefulWidget implements PreferredSizeWidget { class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// Creates a material design app bar. /// Creates a material design app bar.
/// ///
/// The arguments [elevation], [primary], [toolbarOpacity], [bottomOpacity]
/// and [automaticallyImplyLeading] must not be null.
///
/// Typically used in the [Scaffold.appBar] property. /// Typically used in the [Scaffold.appBar] property.
AppBar({ AppBar({
Key key, Key key,
this.leading, this.leading,
this.automaticallyImplyLeading: true,
this.title, this.title,
this.actions, this.actions,
this.flexibleSpace, this.flexibleSpace,
...@@ -143,7 +149,8 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -143,7 +149,8 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
this.centerTitle, this.centerTitle,
this.toolbarOpacity: 1.0, this.toolbarOpacity: 1.0,
this.bottomOpacity: 1.0, this.bottomOpacity: 1.0,
}) : assert(elevation != null), }) : assert(automaticallyImplyLeading != null),
assert(elevation != null),
assert(primary != null), assert(primary != null),
assert(toolbarOpacity != null), assert(toolbarOpacity != null),
assert(bottomOpacity != null), assert(bottomOpacity != null),
...@@ -152,13 +159,21 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -152,13 +159,21 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// A widget to display before the [title]. /// A widget to display before the [title].
/// ///
/// If this is null, the [AppBar] will imply an appropriate widget. For /// If this is null and [automaticallyImplyLeading] is set to true, the [AppBar] will
/// example, if the [AppBar] is in a [Scaffold] that also has a [Drawer], the /// imply an appropriate widget. For example, if the [AppBar] is in a [Scaffold]
/// [Scaffold] will fill this widget with an [IconButton] that opens the /// that also has a [Drawer], the [Scaffold] will fill this widget with an
/// drawer. If there's no [Drawer] and the parent [Navigator] can go back, the /// [IconButton] that opens the drawer. If there's no [Drawer] and the parent
/// [AppBar] will use a [BackButton] that calls [Navigator.maybePop]. /// [Navigator] can go back, the [AppBar] will use a [BackButton] that calls
/// [Navigator.maybePop].
final Widget leading; final Widget leading;
/// Controls whether we should try to imply the leading widget if null.
///
/// If true and [leading] is null, automatically try to deduce what the leading
/// widget should be. If false and [leading] is null, leading space is given to [title].
/// If leading widget is not null, this parameter has no effect.
final bool automaticallyImplyLeading;
/// The primary widget displayed in the appbar. /// The primary widget displayed in the appbar.
/// ///
/// Typically a [Text] widget containing a description of the current contents /// Typically a [Text] widget containing a description of the current contents
...@@ -332,7 +347,7 @@ class _AppBarState extends State<AppBar> { ...@@ -332,7 +347,7 @@ class _AppBarState extends State<AppBar> {
} }
Widget leading = widget.leading; Widget leading = widget.leading;
if (leading == null) { if (leading == null && widget.automaticallyImplyLeading) {
if (hasDrawer) { if (hasDrawer) {
leading = new IconButton( leading = new IconButton(
icon: const Icon(Icons.menu), icon: const Icon(Icons.menu),
...@@ -499,6 +514,7 @@ class _FloatingAppBarState extends State<_FloatingAppBar> { ...@@ -499,6 +514,7 @@ class _FloatingAppBarState extends State<_FloatingAppBar> {
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
_SliverAppBarDelegate({ _SliverAppBarDelegate({
@required this.leading, @required this.leading,
@required this.automaticallyImplyLeading,
@required this.title, @required this.title,
@required this.actions, @required this.actions,
@required this.flexibleSpace, @required this.flexibleSpace,
...@@ -521,6 +537,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { ...@@ -521,6 +537,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
_bottomHeight = bottom?.preferredSize?.height ?? 0.0; _bottomHeight = bottom?.preferredSize?.height ?? 0.0;
final Widget leading; final Widget leading;
final bool automaticallyImplyLeading;
final Widget title; final Widget title;
final List<Widget> actions; final List<Widget> actions;
final Widget flexibleSpace; final Widget flexibleSpace;
...@@ -562,6 +579,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { ...@@ -562,6 +579,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
toolbarOpacity: toolbarOpacity, toolbarOpacity: toolbarOpacity,
child: new AppBar( child: new AppBar(
leading: leading, leading: leading,
automaticallyImplyLeading: automaticallyImplyLeading,
title: title, title: title,
actions: actions, actions: actions,
flexibleSpace: flexibleSpace, flexibleSpace: flexibleSpace,
...@@ -583,6 +601,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { ...@@ -583,6 +601,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
@override @override
bool shouldRebuild(covariant _SliverAppBarDelegate oldDelegate) { bool shouldRebuild(covariant _SliverAppBarDelegate oldDelegate) {
return leading != oldDelegate.leading return leading != oldDelegate.leading
|| automaticallyImplyLeading != oldDelegate.automaticallyImplyLeading
|| title != oldDelegate.title || title != oldDelegate.title
|| actions != oldDelegate.actions || actions != oldDelegate.actions
|| flexibleSpace != oldDelegate.flexibleSpace || flexibleSpace != oldDelegate.flexibleSpace
...@@ -660,9 +679,13 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { ...@@ -660,9 +679,13 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
/// * <https://material.google.com/layout/structure.html#structure-toolbars> /// * <https://material.google.com/layout/structure.html#structure-toolbars>
class SliverAppBar extends StatefulWidget { class SliverAppBar extends StatefulWidget {
/// Creates a material design app bar that can be placed in a [CustomScrollView]. /// Creates a material design app bar that can be placed in a [CustomScrollView].
///
/// The arguments [forceElevated], [primary], [floating], [pinned], [snap]
/// and [automaticallyImplyLeading] must not be null.
const SliverAppBar({ const SliverAppBar({
Key key, Key key,
this.leading, this.leading,
this.automaticallyImplyLeading: true,
this.title, this.title,
this.actions, this.actions,
this.flexibleSpace, this.flexibleSpace,
...@@ -679,7 +702,8 @@ class SliverAppBar extends StatefulWidget { ...@@ -679,7 +702,8 @@ class SliverAppBar extends StatefulWidget {
this.floating: false, this.floating: false,
this.pinned: false, this.pinned: false,
this.snap: false, this.snap: false,
}) : assert(forceElevated != null), }) : assert(automaticallyImplyLeading != null),
assert(forceElevated != null),
assert(primary != null), assert(primary != null),
assert(floating != null), assert(floating != null),
assert(pinned != null), assert(pinned != null),
...@@ -690,13 +714,21 @@ class SliverAppBar extends StatefulWidget { ...@@ -690,13 +714,21 @@ class SliverAppBar extends StatefulWidget {
/// A widget to display before the [title]. /// A widget to display before the [title].
/// ///
/// If this is null, the [AppBar] will imply an appropriate widget. For /// If this is null and [automaticallyImplyLeading] is set to true, the [AppBar] will
/// example, if the [AppBar] is in a [Scaffold] that also has a [Drawer], the /// imply an appropriate widget. For example, if the [AppBar] is in a [Scaffold]
/// [Scaffold] will fill this widget with an [IconButton] that opens the /// that also has a [Drawer], the [Scaffold] will fill this widget with an
/// drawer. If there's no [Drawer] and the parent [Navigator] can go back, the /// [IconButton] that opens the drawer. If there's no [Drawer] and the parent
/// [AppBar] will use an [IconButton] that calls [Navigator.pop]. /// [Navigator] can go back, the [AppBar] will use a [BackButton] that calls
/// [Navigator.maybePop].
final Widget leading; final Widget leading;
/// Controls whether we should try to imply the leading widget if null.
///
/// If true and [leading] is null, automatically try to deduce what the leading
/// widget should be. If false and [leading] is null, leading space is given to [title].
/// If leading widget is not null, this parameter has no effect.
final bool automaticallyImplyLeading;
/// The primary widget displayed in the appbar. /// The primary widget displayed in the appbar.
/// ///
/// Typically a [Text] widget containing a description of the current contents /// Typically a [Text] widget containing a description of the current contents
...@@ -893,6 +925,7 @@ class _SliverAppBarState extends State<SliverAppBar> with TickerProviderStateMix ...@@ -893,6 +925,7 @@ class _SliverAppBarState extends State<SliverAppBar> with TickerProviderStateMix
pinned: widget.pinned, pinned: widget.pinned,
delegate: new _SliverAppBarDelegate( delegate: new _SliverAppBarDelegate(
leading: widget.leading, leading: widget.leading,
automaticallyImplyLeading: widget.automaticallyImplyLeading,
title: widget.title, title: widget.title,
actions: widget.actions, actions: widget.actions,
flexibleSpace: widget.flexibleSpace, flexibleSpace: widget.flexibleSpace,
......
...@@ -790,6 +790,18 @@ void main() { ...@@ -790,6 +790,18 @@ void main() {
expect(find.byIcon(Icons.menu), findsOneWidget); expect(find.byIcon(Icons.menu), findsOneWidget);
}); });
testWidgets('AppBar does not draw menu for drawer if automaticallyImplyLeading is false', (WidgetTester tester) async {
await tester.pumpWidget(
new MaterialApp(
home: new Scaffold(
drawer: const Drawer(),
appBar: new AppBar(automaticallyImplyLeading: false),
),
),
);
expect(find.byIcon(Icons.menu), findsNothing);
});
testWidgets('AppBar handles loose children 0', (WidgetTester tester) async { testWidgets('AppBar handles loose children 0', (WidgetTester tester) async {
final GlobalKey key = new GlobalKey(); final GlobalKey key = new GlobalKey();
await tester.pumpWidget( await tester.pumpWidget(
......
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