demo 地址: https://github.com/iotjin/jh_flutter_demo
代码不定时更新,请前往github查看最新代码
[BaseAppBar 组件实现代码]: https://github.com/iotjin/jh_flutter_demo/blob/master/lib/base_appbar.dart)
序
项目中一般每个页面都要使用导航条,封装成一个组件再调用可减少代码量,方便同一管理
支持
- 左右item设置图片、文字、自定义
- 左侧返回按钮拦截
- 设置背景颜色、透明背景色、渐变背景色
- 标题换行展示、主副标题
- 状态栏处理
- 暗黑模式适配和主题切换
使用的三方库
# 路由管理 https://pub.flutter-io.cn/packages/fluro
fluro: ^2.0.3
# 状态管理 https://pub.flutter-io.cn/packages/provider
provider: ^6.0.3
# 获取安卓iOS设备信息 https://pub.flutter-io.cn/packages/device_info_plus
device_info_plus: ^4.1.2
注: 代码中针对路由、状态栏、主题适配做了二次封装,涉及较多,如需查看请前往demo查看其他组件和使用实例的详细代码,
BaseAppBar
(以下代码)只展示导航条组件代码
实现效果
BaseRefreshView代码
/// base_appbar.dart
///
/// Created by iotjin on 2020/03/10.
/// description: 导航条基类
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'jh_common/utils/jh_status_bar_utils.dart';
import 'project/configs/colors.dart';
import 'project/provider/theme_provider.dart';
const double _elevation = 0;
const double _titleFontSize = 18.0;
const double _textFontSize = 16.0;
const double _itemSpace = 15.0; // 右侧item内间距
const double _imgWH = 22.0; // 右侧图片宽高
const double _rightSpace = 5.0; // 右侧item右间距
// 默认颜色
const Color _bgColor = KColors.kNavThemeBgColor;
const Color _bgDarkColor = KColors.kNavBgDarkColor;
const Color _titleColor = KColors.kNavTitleColor;
// 状态栏字体颜色,当backgroundColor透明或者是白色,状态栏字体为黑色,暗黑模式为白色
const Brightness _brightness = Brightness.light;
const Color appbarStartColor = KColors.kGradientStartColor; // 默认appBar 渐变开始色
const Color appbarEndColor = KColors.kGradientEndColor; // 默认appBar 渐变结束色
/// 渐变导航条
class GradientAppBar extends StatefulWidget implements PreferredSizeWidget {
const GradientAppBar(
this.title, {
Key? key,
this.rightText,
this.rightImgPath,
this.leftWidget,
this.titleWidget,
this.rightWidgets,
this.brightness = _brightness,
this.elevation: _elevation,
this.bottomWidget,
this.leftItemCallBack,
this.rightItemCallBack,
}) : super(key: key);
final String title;
final String? rightText;
final String? rightImgPath;
final Widget? leftWidget;
final Widget? titleWidget;
final List<Widget>? rightWidgets;
final Brightness brightness;
final double elevation;
final PreferredSizeWidget? bottomWidget;
final Function? leftItemCallBack;
final Function? rightItemCallBack;
State<GradientAppBar> createState() => _GradientAppBarState();
Size get preferredSize => Size.fromHeight(kToolbarHeight + (bottomWidget?.preferredSize.height ?? 0.0));
}
class _GradientAppBarState extends State<GradientAppBar> {
Widget build(BuildContext context) {
var flexibleSpace = Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [appbarStartColor, appbarEndColor],
),
),
);
return BaseAppBar(
widget.title,
rightText: widget.rightText,
rightImgPath: widget.rightImgPath,
leftWidget: widget.leftWidget,
titleWidget: widget.titleWidget,
rightWidgets: widget.rightWidgets,
bgColor: Colors.white.withOpacity(0),
elevation: widget.elevation,
bottomWidget: widget.bottomWidget,
rightItemCallBack: widget.rightItemCallBack,
leftItemCallBack: widget.leftItemCallBack,
flexibleSpace: flexibleSpace,
);
}
}
/// 导航条基类
class BaseAppBar extends StatefulWidget implements PreferredSizeWidget {
const BaseAppBar(
this.title, {
Key? key,
this.rightText,
this.rightImgPath,
this.leftWidget,
this.titleWidget,
this.rightWidgets,
this.bgColor,
this.brightness = _brightness,
this.elevation: _elevation,
this.bottomWidget,
this.flexibleSpace,
this.leftItemCallBack,
this.rightItemCallBack,
}) : super(key: key);
final String title; // 标题文字
final String? rightText; // 右侧按钮文字
final String? rightImgPath; // 右侧按钮图片路径,优先级高于rightText
final Widget? leftWidget; // 左侧Widget,为空显示返回按钮
final Widget? titleWidget; // 标题Widget,优先级高于title
final List<Widget>? rightWidgets; // 优先级高于rightText和rightImgPath
final Color? bgColor; // 背景颜色,默认主题色,设置的颜色优先级高于暗黑模式
final Brightness brightness;
final double elevation;
final PreferredSizeWidget? bottomWidget;
final Widget? flexibleSpace;
final Function? leftItemCallBack;
final Function? rightItemCallBack;
State<BaseAppBar> createState() => _BaseAppBarState();
Size get preferredSize => Size.fromHeight(kToolbarHeight + (bottomWidget?.preferredSize.height ?? 0.0));
}
class _BaseAppBarState extends State<BaseAppBar> {
Widget build(BuildContext context) {
return _appBar();
}
Widget _appBar() {
// 默认颜色
Color titleAndIconColor = _titleColor;
Color bgColor = widget.bgColor ?? _bgColor;
var brightness = widget.brightness;
// 如果背景透明或者是白色,设置字体和图标、状态栏字体为黑色
if (widget.bgColor == Colors.transparent ||
widget.bgColor == Colors.white ||
widget.bgColor == KColors.kNavWhiteBgColor) {
titleAndIconColor = Colors.black;
brightness = Brightness.dark;
} else {
brightness = Brightness.light;
}
// TODO: 通过ThemeProvider进行主题管理
final provider = Provider.of<ThemeProvider>(context);
final bool isDark = context.jhIsDark;
// 设置的颜色优先级高于暗黑模式
bgColor = widget.bgColor ?? (isDark ? _bgDarkColor : provider.getThemeColor());
if (isDark) {
titleAndIconColor = _titleColor;
}
// 标题
var titleWidget = widget.titleWidget ??
Text(widget.title, style: TextStyle(fontSize: _titleFontSize, color: titleAndIconColor), maxLines: 2);
// 左侧
var backWidget = IconButton(
// icon: Icon(Icons.arrow_back_ios,color: _color),
icon: ImageIcon(
AssetImage('assets/images/common/ic_nav_back_white.png'),
color: titleAndIconColor,
),
iconSize: 18,
padding: const EdgeInsets.fromLTRB(0, 0, 10, 0),
onPressed: () {
if (widget.leftItemCallBack == null) {
_popThis(context);
} else {
widget.leftItemCallBack!();
}
},
);
var leftWidget = widget.leftWidget ?? backWidget;
// 右侧
Widget rightWidget = Text('');
if (widget.rightText != null) {
rightWidget = InkWell(
child: Container(
margin: EdgeInsets.all(_itemSpace),
color: Colors.transparent,
child: Center(
child: Text(widget.rightText!, style: TextStyle(fontSize: _textFontSize, color: titleAndIconColor)),
),
),
onTap: () => widget.rightItemCallBack?.call(),
);
}
if (widget.rightImgPath != null) {
rightWidget = IconButton(
icon: Image.asset(widget.rightImgPath!, width: _imgWH, height: _imgWH, color: titleAndIconColor),
onPressed: () => widget.rightItemCallBack?.call(),
);
}
var actions = [
Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[rightWidget, SizedBox(width: _rightSpace)],
),
];
var rightWidgets = widget.rightWidgets ?? actions;
return AppBar(
title: titleWidget,
centerTitle: true,
backgroundColor: bgColor,
systemOverlayStyle: JhStatusBarUtils.getStatusBarStyle(isDark: isDark, brightness: brightness),
bottom: widget.bottomWidget,
elevation: widget.elevation,
leading: leftWidget,
actions: rightWidgets,
flexibleSpace: widget.flexibleSpace,
);
}
}
void _popThis(BuildContext context) {
if (Navigator.of(context).canPop()) {
FocusManager.instance.primaryFocus?.unfocus();
Navigator.of(context).pop();
}
}
// 多行标题
class TwoLinesTitle extends StatelessWidget {
const TwoLinesTitle({
Key? key,
this.title = '',
this.subtitle = '',
this.color = Colors.white,
}) : super(key: key);
final String title;
final String subtitle;
final Color color;
Widget build(BuildContext context) {
Widget widget;
if (subtitle.isEmpty) {
widget = Text(title, style: TextStyle(fontSize: _titleFontSize, color: color));
} else {
widget = RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: title,
style: TextStyle(fontSize: 20, color: color),
children: <TextSpan>[
TextSpan(text: '\n$subtitle', style: TextStyle(color: color, fontSize: 14)),
],
),
);
}
return widget;
}
}