Flutter 之 AppBar 这样的骚操作你知道吗?

2023-10-23 14:32
文章标签 操作 flutter 知道 appbar

本文主要是介绍Flutter 之 AppBar 这样的骚操作你知道吗?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

好久不见了,这阵子在忙公司的项目,加班比较严重,这周终于抽了点时间来帮国外一家公司做一款跨平台的 App。由于去年九月份在上海参加过 Google 举办的 Google develop days, 受益颇多,特别在其目前正在大力热推的 Flutter 框架。相比于目前热门的跨平台框架 React Native,Flutter在 UI 绘制以及性能方便不遑多让。因此,这款 app 打算基于 Dart 语言,并采用 Flutter 框架来完成。

花了大概几天时间熟悉了下 Dart 语法和 Flutter 基本组成控件,就开始摸索着做一个练手项目。AppBar 想必大家都用过,当其处于启动页时是隐藏的,而处于其他页面时左边默认携带来返回按钮。目前,我们的需求是:首页的 AppBar 最左边为用户头像,点击用户头像可以自动打开左侧抽屉栏。

最终效果图如下所示:

效果图

初学者一开始总是痛苦的,还好,解决问题的途径“万变不离其宗”。我们先来查看一下 Flutter 官方文档 发现,要使用 AppBar来操作页面,决定其左边点击事件的属性就是 leading

AppBar({
Key key,
Widget leading,
bool automaticallyImplyLeading: true,
Widget title,
List<Widget> actions,
Widget flexibleSpace,
PreferredSizeWidget bottom,
double elevation,
Color backgroundColor,
Brightness brightness,
IconThemeData iconTheme,
TextTheme textTheme,
bool primary: true,
bool centerTitle,
double titleSpacing: NavigationToolbar.kMiddleSpacing,
double toolbarOpacity: 1.0,
double bottomOpacity: 1.0
})

可以看到,我们 AppBar 中的 leading 属性是一个 Widget 类型,那么它接收的参数范围就很广了,换句话说,我们可以直接将一个按钮传递给它,并在 onPressed 方法中处理左边侧滑栏的开启和关闭状态。?,有思路了咱们久开始试验一下:

 final GlobalKey<ScaffoldState> _scaffoldKey = new 													GlobalKey();@overrideWidget build(BuildContext context) {return new DefaultTabController(length: _pages.length,child: new Scaffold(key: _scaffoldKey,appBar: new AppBar(/// 第一种方式/// 通过可监听点击的IconButton传入widget,/// 并在onPressed中处理drawer开启,借助于GlobalKeyleading: new IconButton(icon: new Container(padding: EdgeInsets.all(3.0),child: new CircleAvatar(radius: 30.0,backgroundImage: AssetImage("assets/images/moosphon_logo.jpeg")),),onPressed: (){_scaffoldKey.currentState.openDrawer();},),centerTitle: true,title: new TabBar(isScrollable: true,labelPadding: EdgeInsets.all(14),indicatorSize: TabBarIndicatorSize.label,indicatorColor: Colors.white,tabs: _titles.map((String title) =>new Tab(text: title)).toList()),actions: <Widget>[new IconButton(icon: new Icon(Icons.search),tooltip: '搜索',onPressed: (){NavigatorUtil.intentToPage(context, new SearchPage(), pageName: "SearchPage");})],),body: new TabBindingView(),drawer: new Drawer(child: new HomeLeftDrawerPage(),),));}

为了不影响篇幅,这里我只贴了关键代码,至于 Drawer 用法相信大家没什么问题,这里我们给 AppBar 的leading 参数传了一个 IconButton 控件,里面我还实现了圆形头像。这样一来,我们的用户头像就可以被点击了响应了,接下来我们需要处理头像点击事件与 Drawer 的联动性了。这里我们通过设置 GlobalKey 来让 Scaffold 容器获取到其内部的 Drawer 组件,进而控制它的开闭,这样,我们就已经可以通过点击自定义的用户头像来开启左边侧滑栏啦?。

细心的小伙伴一定会发现,上面的代码中我注释这只是第一种实现方式,难道还有第二种实现方式?哈哈,没错,的确还有第二种思路。其实,通过查看 AppBar 的源码,我们可以发现:AppBar内部已经自动实现了 AppBar 的 leadingDrawer 抽屉栏的关联:

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 = ConstrainedBox(constraints: const BoxConstraints.tightFor(width: _kLeadingWidth),child: leading,);}

由于代码较多,这里我只放上了 build 方法中关于 leading 部分的关键代码,通过分析我们可以发现:

  • leading 如果 为空并且 automaticallyImplyLeading 属性为true,那么就会自动推断出 leadingWidget 的类型和用途,另外如果当前 Scaffold 中存在 Drawer ,则会自动创建一个 IconButton 作为leading 使用,同时它的点击事件中处理了与 Drawer 抽屉栏的关联事件,无需开发者再处理。也就是说,在这种情况下,如果我们自定义了 leading (例如当前我们给它传的是用户的头像 Widget ),那么 leading 就无法自动关联 Drawer ,也就是说关联 Drawer 的这部分代码需要开发者自行实现。
  • 如果 leading 不为空呢?并且 automaticallyImplyLeading 开关关闭,那么 leading 的空间就会被 title 给占据。

说了这么多,最终的结论是:如果我们想要自定义 leading ,那么目前官方源码中 AppBar 中 leading 自动关联 Drawer 的处理我们没办法使用。如果我们非要用呢?那就只能修改源码啦:

/// 注意,前方高能来袭,以下为修改的部分codeWidget leading = widget.leading;if (/*leading == null && */widget.automaticallyImplyLeading) {if (hasDrawer) {leading = IconButton(icon: /*const Icon(Icons.menu)*/ leading ?? Icon(Icons.home), // 如果leading指定了widget那么onPressed: _handleDrawerButton,tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,);} else {if (canPop)leading = useCloseButton ? const CloseButton() : const BackButton();}}if (leading != null) {leading = ConstrainedBox(constraints: const BoxConstraints.tightFor(width: _kLeadingWidth),child: leading,);}

铛铛铛铛~这就改好啦,改动的地方是不是很少?哈哈?,其实只要把 leading 为空的限制条件去掉,并且可以传入我们自己定义的 Widget 就好啦!

这样一来,我们就不用手动去处理 leadingDrawer 的关联事件,只需要交给系统帮我们完成就可以啦:

@overrideWidget build(BuildContext context) {return new DefaultTabController(length: _pages.length,child: new Scaffold(key: _scaffoldKey,appBar: new DrawerAutoBindingAppBar(/// 第二种方式/// 通过修改[AppBar]源码来将普通widget作为icon传给IconButton/// 源码中已处理onPressed关联drawer事件,无需额外处理,详情见[DrawerAutoBindingAppBar]leading: new Container(padding: EdgeInsets.all(3.0),child: new CircleAvatar(radius: 30.0,backgroundImage: AssetImage("assets/images/moosphon_logo.jpeg"))),centerTitle: true,title: new TabBar(isScrollable: true,labelPadding: EdgeInsets.all(14),indicatorSize: TabBarIndicatorSize.label,indicatorColor: Colors.white,tabs: _titles.map((String title) =>new Tab(text: title)).toList()),actions: <Widget>[new IconButton(icon: new Icon(Icons.search),tooltip: '搜索',onPressed: (){NavigatorUtil.intentToPage(context, new SearchPage(), pageName: "SearchPage");})],),body: new TabBindingView(),drawer: new Drawer(child: new HomeLeftDrawerPage(),),));}

代码就不多做解释了,效果是和之前一样的,好啦,这样我们就更加了解 AppBar 的用法以及 leading 的背后“小彩蛋”啦?。

感谢大家的阅读,以后我将给大家带来更多好玩实用的应用型开发技巧,如果大家有任何建议,欢迎留言?。

QQ群: 601924443 一起玩技术~

这篇关于Flutter 之 AppBar 这样的骚操作你知道吗?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/268474

相关文章

C# WinForms存储过程操作数据库的实例讲解

《C#WinForms存储过程操作数据库的实例讲解》:本文主要介绍C#WinForms存储过程操作数据库的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、存储过程基础二、C# 调用流程1. 数据库连接配置2. 执行存储过程(增删改)3. 查询数据三、事务处

Java使用Curator进行ZooKeeper操作的详细教程

《Java使用Curator进行ZooKeeper操作的详细教程》ApacheCurator是一个基于ZooKeeper的Java客户端库,它极大地简化了使用ZooKeeper的开发工作,在分布式系统... 目录1、简述2、核心功能2.1 CuratorFramework2.2 Recipes3、示例实践3

Java利用JSONPath操作JSON数据的技术指南

《Java利用JSONPath操作JSON数据的技术指南》JSONPath是一种强大的工具,用于查询和操作JSON数据,类似于SQL的语法,它为处理复杂的JSON数据结构提供了简单且高效... 目录1、简述2、什么是 jsONPath?3、Java 示例3.1 基本查询3.2 过滤查询3.3 递归搜索3.4

Flutter打包APK的几种方式小结

《Flutter打包APK的几种方式小结》Flutter打包不同于RN,Flutter可以在AndroidStudio里编写Flutter代码并最终打包为APK,本篇主要阐述涉及到的几种打包方式,通... 目录前言1. android原生打包APK方式2. Flutter通过原生工程打包方式3. Futte

Python使用DrissionPage中ChromiumPage进行自动化网页操作

《Python使用DrissionPage中ChromiumPage进行自动化网页操作》DrissionPage作为一款轻量级且功能强大的浏览器自动化库,为开发者提供了丰富的功能支持,本文将使用Dri... 目录前言一、ChromiumPage基础操作1.初始化Drission 和 ChromiumPage

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

Redis中管道操作pipeline的实现

《Redis中管道操作pipeline的实现》RedisPipeline是一种优化客户端与服务器通信的技术,通过批量发送和接收命令减少网络往返次数,提高命令执行效率,本文就来介绍一下Redis中管道操... 目录什么是pipeline场景一:我要向Redis新增大批量的数据分批处理事务( MULTI/EXE

使用Python高效获取网络数据的操作指南

《使用Python高效获取网络数据的操作指南》网络爬虫是一种自动化程序,用于访问和提取网站上的数据,Python是进行网络爬虫开发的理想语言,拥有丰富的库和工具,使得编写和维护爬虫变得简单高效,本文将... 目录网络爬虫的基本概念常用库介绍安装库Requests和BeautifulSoup爬虫开发发送请求解

Oracle存储过程里操作BLOB的字节数据的办法

《Oracle存储过程里操作BLOB的字节数据的办法》该篇文章介绍了如何在Oracle存储过程中操作BLOB的字节数据,作者研究了如何获取BLOB的字节长度、如何使用DBMS_LOB包进行BLOB操作... 目录一、缘由二、办法2.1 基本操作2.2 DBMS_LOB包2.3 字节级操作与RAW数据类型2.

JDK多版本共存并自由切换的操作指南(本文为JDK8和JDK17)

《JDK多版本共存并自由切换的操作指南(本文为JDK8和JDK17)》本文介绍了如何在Windows系统上配置多版本JDK(以JDK8和JDK17为例),并通过图文结合的方式给大家讲解了详细步骤,具有... 目录第一步 下载安装JDK第二步 配置环境变量第三步 切换JDK版本并验证可能遇到的问题前提:公司常