Cap1 前言
在制作 Flutter App 新建一个抽屉的实例后,发现一个问题:打开抽屉发现状态栏是灰色的,主要抽屉头部我用图片展示,理想效果应该是抽屉头部图片偏移上面状态栏的高度。
Cap2 思路
除了查看文档之外,更好的方法直接查看 Drawer
源码,看看它是如何实现的。由于头部灰色原因我想到可能发生在 Drawer
的第一个子级参数:DrawerHeader
,他的作用类似容器展示,直接查看它的源码:
// 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 'package:flutter/widgets.dart';
import 'debug.dart';
import 'divider.dart';
import 'theme.dart';
const double _kDrawerHeaderHeight = 160.0 + 1.0; // bottom edge
/// The top-most region of a material design drawer. The header's [child]
/// widget, if any, is placed inside a [Container] whose [decoration] can be
/// passed as an argument, inset by the given [padding].
///
/// Part of the material design [Drawer].
///
/// Requires one of its ancestors to be a [Material] widget. This condition is
/// satisfied by putting the [DrawerHeader] in a [Drawer].
///
/// See also:
///
/// * [UserAccountsDrawerHeader], a variant of [DrawerHeader] that is
/// specialized for showing user accounts.
/// * <https://material.io/design/components/navigation-drawer.html>
class DrawerHeader extends StatelessWidget {
/// Creates a material design drawer header.
///
/// Requires one of its ancestors to be a [Material] widget.
const DrawerHeader({
Key key,
this.decoration,
this.margin = const EdgeInsets.only(bottom: 8.0),
this.padding = const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 8.0),
this.duration = const Duration(milliseconds: 250),
this.curve = Curves.fastOutSlowIn,
@required this.child,
}) : super(key: key);
/// Decoration for the main drawer header [Container]; useful for applying
/// backgrounds.
///
/// 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 EdgeInsetsGeometry padding;
/// The margin around the drawer header.
final EdgeInsetsGeometry margin;
/// The duration for animations of the [decoration].
final Duration duration;
/// The curve for animations of the [decoration].
final Curve curve;
/// A widget to be placed inside the drawer header, inset by the [padding].
///
/// This widget will be sized to the size of the header. To position the child
/// precisely, consider using an [Align] or [Center] widget.
///
/// {@macro flutter.widgets.child}
final Widget child;
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
assert(debugCheckHasMediaQuery(context));
final ThemeData theme = Theme.of(context);
final double statusBarHeight = MediaQuery.of(context).padding.top;
return Container(
height: statusBarHeight + _kDrawerHeaderHeight,
margin: margin,
decoration: BoxDecoration(
border: Border(
bottom: Divider.createBorderSide(context),
),
),
child: AnimatedContainer(
padding: padding.add(EdgeInsets.only(top: statusBarHeight)),
decoration: decoration,
duration: duration,
curve: curve,
child: child == null ? null : DefaultTextStyle(
style: theme.textTheme.body2,
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: child,
),
),
),
);
}
}
在第 79 行指示出这边这边有个获取状态栏高度的方法,然后再加其他一些边距,这边逻辑走一通没什么问题,那应该问题不出现在这边,那么则判断父类容器的问题,直接修改成:
Drawer buildDrawer(BuildContext context, String currentRoute) {
return new Drawer(
child: new Text('test')
);
}
发现不会出现灰色状态栏的问题,初步判断原因在这里面,继续查看 ListView
里面的源码:
@override
List<Widget> buildSlivers(BuildContext context) {
Widget sliver = buildChildLayout(context);
EdgeInsetsGeometry effectivePadding = padding;
if (padding == null) {
final MediaQueryData mediaQuery = MediaQuery.of(context, nullOk: true);
if (mediaQuery != null) {
// Automatically pad sliver with padding from MediaQuery.
final EdgeInsets mediaQueryHorizontalPadding =
mediaQuery.padding.copyWith(top: 0.0, bottom: 0.0);
final EdgeInsets mediaQueryVerticalPadding =
mediaQuery.padding.copyWith(left: 0.0, right: 0.0);
// Consume the main axis padding with SliverPadding.
effectivePadding = scrollDirection == Axis.vertical
? mediaQueryVerticalPadding
: mediaQueryHorizontalPadding;
// Leave behind the cross axis padding.
sliver = MediaQuery(
data: mediaQuery.copyWith(
padding: scrollDirection == Axis.vertical
? mediaQueryHorizontalPadding
: mediaQueryVerticalPadding,
),
child: sliver,
);
}
}
if (effectivePadding != null)
sliver = SliverPadding(padding: effectivePadding, sliver: sliver);
return <Widget>[ sliver ];
}
- 当
ListView
的属性padding
为空时,获取MediaQueryData
的信息 - 因为
ListView
的滚动方向默认为垂直,会使用mediaQueryVerticalPadding
sliver
添加一层MediaQuery
,这个表明sliver
的子部件会使用该MediaQuery
的值,根据判断,子部件会使用mediaQueryHorizontalPadding
,而上面的两个复制:
mediaQueryHorizontalPadding
=> 将原有的MediaQuery
的padding
复制为top
和bottom
都为0
,该值会被子部件使用,所以DrawerHeader
使用了该值,导致statusBarHeader
为0
mediaQueryVerticalPadding
=> 将原有的MediaQuery
的padding
复制为left
和right
都为0
所以添加 ListView
的 padding
属性即可:
new Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[]
)
)
安装Flutter环境
Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。

编写第一个Flutter App
从编写这篇博文就有个写个App的计划,大概是Flutter功能展示方向的App,相信在Github有了一大堆轮子项目,且当对我来说是一个锻炼技术的项目吧,希望在这个项目过程中能够给自己带来App开发思考和设计理念。
