有点坑的leading
我在项目中遇到这样一个问题,设置appBar
的 leading
的时候报错,我定义的leading widget结构如下图红框中所示:
然而报错、报错一直报错,大致错误日志是超出多少像素,我依次减少红框中文字的字符数,报错log中提示的超出数量越来越小,看起来像是leading被限制了宽度?
flutter是开源的,这真是极好的,点进去看看...
class AppBar extends StatefulWidget implements PreferredSizeWidget
AppBar({
Key key,
this.leading,
)}
直接看重点位置,即创建leading的位置
Widget leading = widget.leading;
if (leading == null && widget.automaticallyImplyLeading) {
if (hasDrawer) {
leading = IconButton(
icon: const Icon(Icons.menu),
onPressed: _handleDrawerButton,
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
);
} else {
if (canPop)
leading = useCloseButton ? const CloseButton() : const BackButton();
}
}
if (leading != null) {
// 注意此处,leading被设置为一个最大宽度 _kLeadingWidth,看起来我们的猜测是正确的。
leading = ConstrainedBox(
constraints: const BoxConstraints.tightFor(width: _kLeadingWidth),
child: leading,
);
}
那么 _kLeadingWidth又是什么多少?依次寻找往下点
const double _kLeadingWidth = kToolbarHeight; // So the leading button is square.
/// The height of the toolbar component of the [AppBar].
const double kToolbarHeight = 56.0;
很明显,leading被限制了最宽56,那么默认的AppBar很明显无法满足我的需求,so,自定义走起。
自定义AppBar
上面的代码中可以看到,AppBar只是实现了 PreferredSizeWidget接口
class AppBar extends StatefulWidget implements PreferredSizeWidget
那么我们也可以从这进行入手,自定义一个实现了 PreferredSizeWidget的Widget
具体代码并不多,比你想象的简单,直接上代码,
import 'package:flutter/material.dart';
/// 这是一个可以指定SafeArea区域背景色的AppBar
/// PreferredSizeWidget提供指定高度的方法
/// 如果没有约束其高度,则会使用PreferredSizeWidget指定的高度
class CustomAppbar extends StatefulWidget implements PreferredSizeWidget {
final double contentHeight; //从外部指定高度
Color navigationBarBackgroundColor; //设置导航栏背景的颜色
Widget leadingWidget;
Widget trailingWidget;
String title;
CustomAppbar({
@required this.leadingWidget,
@required this.title,
this.contentHeight = 44,
this.navigationBarBackgroundColor = Colors.white,
this.trailingWidget,
}) : super();
@override
State<StatefulWidget> createState() {
return new _CustomAppbarState();
}
@override
Size get preferredSize => new Size.fromHeight(contentHeight);
}
/// 这里没有直接用SafeArea,而是用Container包装了一层
/// 因为直接用SafeArea,会把顶部的statusBar区域留出空白
/// 外层Container会填充SafeArea,指定外层Container背景色也会覆盖原来SafeArea的颜色
/// var statusheight = MediaQuery.of(context).padding.top; 获取状态栏高度
class _CustomAppbarState extends State<CustomAppbar> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return new Container(
color: widget.navigationBarBackgroundColor,
child: new SafeArea(
top: true,
child: new Container(
decoration: new UnderlineTabIndicator(
borderSide: BorderSide(width: 1.0, color: Color(0xFFeeeeee)),
),
height: widget.contentHeight,
child: new Stack(
alignment: Alignment.center,
children: <Widget>[
Positioned(
left: 0,
child: new Container(
padding: const EdgeInsets.only(left: 5),
child: widget.leadingWidget,
),
),
new Container(
child: new Text(widget.title,
style: new TextStyle(
fontSize: 17, color: Color(0xFF333333))),
),
Positioned(
right: 0,
child: new Container(
padding: const EdgeInsets.only(right: 5),
child: widget.trailingWidget,
),
),
],
)),
),
);
}
}
引用的地方:
appBar: new CustomAppbar(
title: '日历',
leadingWidget: leftBarButtonItemWidget(),
trailingWidget: rightBarButtonItemsWidget(),
)
leftBarButtonItemWidget()
rightBarButtonItemsWidget()
两个方法是我自定义的导航栏按钮,实现自己需要的即可。值得说的是,可以将leadingWidget设置为非@required的,在自定义的AppBar里面做好处理即可,另外在上面的代码中并没有限制导航栏上每个Widget所占用的最大空间,如果你的导航栏子组件可能很宽,提前进行妥善处理是个好主意。