摘要:侧边栏我们先图解一下侧边栏的结构整个侧边栏主从上到下按区块分别放置了账号和若干功能项分割线的列表,很容易想到使用布局控件。账号信息区域中有账号头像粉丝头像账号文字信息和背景图,这块我们可以使用控件库的控件实现。
经过2周的学习,看过笔记1-8的小伙伴们已经有不少开始自己写APP了,我也按耐不住这股热情,想要自己开发个APP玩玩,so,从本篇起,仿造一个APP,项目从0开始,每篇增加一些内容,一点一点完成这个APP,每次迭代的代码都将上传到我的git仓库。
鉴于我2周多的Flutter代码经验,代码结构的思维可能没有多年开发经验的老鸟稳,如果有写的不好的地方请大家多多指教。
如上图所示, 本篇将搭建一个HomePage,再其左上角加入侧边栏入口,并且通过侧边栏可以进入其他页面。
第一步创建项目和文件夹。打开vscode,到一个路径下输入命令:
flutter create appbyflutter
根据图中所示,将项目目录准备好:
由于第一篇开发用到的东西不多,先简单向项目目录中添加一个images文件,用于存放APP默认图片。默认的lib文件夹下添加一个pages文件夹,用于存放每个页面。
第二步将main.dart仅作为APP的入口,承担页面入口和路由的功能:
由于APP不只有一个页面,为了方便维护和管理,所有的页面代码都转移到pages文件夹下,main.dart中处理APP的主页面入口、路由和一系列需要初始化(如自动登陆、入场动画等)的任务。有过vue、react开发经验的前端大神们应该不陌生,这样做可以使主程序和页面解耦,当然本篇还没有用到路由,暂不书写路由的代码,等不及要了解路由的同学可以参考前端高手偏罗的第一个APP或者英文阅读理解。
第三步 主页面如第一步的图所示,在pages文件夹中添加了2个文件:home_page.dart和other_page.dart,其中home_page.dart是这个APP的主页面,other_page.dart作为的以后再开发的页面。
注意在第二步的runapp()函数中,用到了MaterialApp(),意味着程序APP所有的页面控件默认配套_Material_风格。
由于主页面会动态引用各种控件,因此_StatefulWidget_类型才可以满足页面需求。从下图中分解一下页面结构:
先看图左中有状态控件HomePage为整个页面的最顶层包裹,其内放入了一个Scaffold脚手架,Scaffold中有非常丰富的属性,可以放入侧边栏按钮Drawer控件、页面标题AppBar控件和body部分,于是贴入以下代码:
import "package:flutter/material.dart"; class HomePage extends StatefulWidget { @override _HomePageState createState() => new _HomePageState(); } class _HomePageState extends State{ @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar(title: new Text("CYC"), backgroundColor: Colors.redAccent,), //头部的标题AppBar drawer: new Drawer(), //侧边栏按钮Drawer body: new Center( //中央内容部分body child: new Text("HomePage",style: new TextStyle(fontSize: 35.0),), ), ); } }
OK,左图的页面就这么轻松搭建完毕。要实现右图中的展开的侧边栏,很简单,向Drawer控件中塞东西吧。
侧边栏我们先图解一下侧边栏的结构:
整个侧边栏主从上到下按区块分别放置了账号和若干功能项+分割线的列表,很容易想到使用布局控件ListView。
账号信息区域中有账号头像、粉丝头像、账号文字信息和背景图,这块我们可以使用Material控件库的UserAccountsDrawerHeader控件实现。
下面的功能列表项目不用多说,ListTitle控件妥妥的,分割线直接Divider即可。
于是,我们向new Drawer()中加入如下代码:
//侧边栏填充内容 drawer: new Drawer( //侧边栏按钮Drawer child: new ListView( children:[ new UserAccountsDrawerHeader( //Material内置控件 accountName: new Text("CYC"), //用户名 accountEmail: new Text("example@126.com"), //用户邮箱 currentAccountPicture: new GestureDetector( //用户头像 onTap: () => print("current user"), child: new CircleAvatar( //圆形图标控件 backgroundImage: new NetworkImage("https://upload.jianshu.io/users/upload_avatars/7700793/dbcf94ba-9e63-4fcf-aa77-361644dd5a87?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240"),//图片调取自网络 ), ), otherAccountsPictures: [ //粉丝头像 new GestureDetector( //手势探测器,可以识别各种手势,这里只用到了onTap onTap: () => print("other user"), //暂且先打印一下信息吧,以后再添加跳转页面的逻辑 child: new CircleAvatar( backgroundImage: new NetworkImage("https://upload.jianshu.io/users/upload_avatars/10878817/240ab127-e41b-496b-80d6-fc6c0c99f291?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240"), ), ), new GestureDetector( onTap: () => print("other user"), child: new CircleAvatar( backgroundImage: new NetworkImage("https://upload.jianshu.io/users/upload_avatars/8346438/e3e45f12-b3c2-45a1-95ac-a608fa3b8960?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240"), ), ), ], decoration: new BoxDecoration( //用一个BoxDecoration装饰器提供背景图片 image: new DecorationImage( fit: BoxFit.fill, // image: new NetworkImage("https://raw.githubusercontent.com/flutter/website/master/_includes/code/layout/lakes/images/lake.jpg") //可以试试图片调取自本地。调用本地资源,需要到pubspec.yaml中配置文件路径 image: new ExactAssetImage("images/lake.jpg"), ), ), ), new ListTile( //第一个功能项 title: new Text("First Page"), trailing: new Icon(Icons.arrow_upward), onTap: () { Navigator.of(context).pop(); Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context) => new SidebarPage())); } ), new ListTile( //第二个功能项 title: new Text("Second Page"), trailing: new Icon(Icons.arrow_right), onTap: () { Navigator.of(context).pop(); Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context) => new SidebarPage())); } ), new ListTile( //第二个功能项 title: new Text("Second Page"), trailing: new Icon(Icons.arrow_right), onTap: () { Navigator.of(context).pop(); Navigator.of(context).pushNamed("/a"); } ), new Divider(), //分割线控件 new ListTile( //退出按钮 title: new Text("Close"), trailing: new Icon(Icons.cancel), onTap: () => Navigator.of(context).pop(), //点击后收起侧边栏 ), ], ), )
上面的代码,用到了很多陌生的控件,如UserAccountsDrawerHeader、GestureDetector、BoxDecoration、NetworkImage、ExactAssetImage等等,这里我就不一一介绍了,各自的特性和用法请参考官方阅读理解题库,刚开始我也是懵逼的,这些内置控件大家简单背诵一下即可,有可能后面因为页面复杂度的提高,多带带拿出来封装也说不定,会使用就可以了。
大家可以试试从屏幕的左边沿向右滑动的手势,是不是发现可以拉出侧边栏?再向右滑动收回侧边栏。我并没有添加任何手势事件的代码,这是Drawer控件自带的属性,和控件自带Material风格动效一样,内置控件也自带了默认手势,隐隐听到~原生开发的程序员哭晕在厕所,哈哈哈第四步 功能按钮触发页面跳转。
首先我们要创建一个子页面,于是乎pages文件夹下,我又创建了一个other_page.dart文件。要从HomePage.dart中跳转到other_page.dart,还需要在HomePage.dart中引一下other_page.dart。于是:
然后到other_page.dart中敲入代码:
import "package:flutter/material.dart"; class OtherPage extends StatelessWidget { final String pageText; //定义一个常量,用于保存跳转进来获取到的参数 OtherPage(this.pageText); //构造函数,获取参数 @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar(title: new Text(pageText),), //将参数当作页面标题 body: new Center( child: new Text("pageText"), ), ); } }
Flutter要求转入的页面必须提前定义一个常量分配好空间,且在构造函数中植入这个参数,才可捕捉外部传过来的参数值。
触发跳转向First Page和Second Page这两个ListTile控件中加入点击跳转页面的代码:
new ListTile( title: new Text("First Page"), trailing: new Icon(Icons.arrow_upward), onTap: () { Navigator.of(context).pop(); //点击后收起侧边栏 Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context) => new OtherPage("First Page"))); //进入OtherPage页面,传入参数First Page } ), new ListTile( title: new Text("Second Page"), trailing: new Icon(Icons.arrow_right), onTap: () { Navigator.of(context).pop(); Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context) => new OtherPage("Second Page"))); } ),
上面的代码中onTap()事件里有一句Navigator.of(context).pop(); ,意味着先收起侧边栏,再进入新页面。如果没有这句代码,即使进入了新页面,再返回来,侧边栏依然处于展开的样子,这个体验是反人类的,所以写上它吧~少年。
总结由于我没有详细的去定位和设计产品到底是干什么的,大家可能会觉得有点懵逼,为什么是这种侧边栏的布局,而不是很多社交APP常用的顶部+底部Tab栏的样式,不着急,我们下一篇实现。侧边栏有什么好处呢?节省空间,如果底部需要放置更重要的功能控件(比如音乐播放器)时,往侧边栏放入页面切换逻辑是个不错的应对方案。本篇内容其实非常简单,主要就是介绍大家认识几个常用控件,不用调CSS,不用思考因为冒泡事件导致复杂的交互逻辑实现,这就是Flutter的魅力,简约而不简单,相信大家看过之后,自行开发APP的信心更足了,好勒,今天就到这里,感谢大家的支持,请关注我的Flutter圈子,多多投稿,也可以加入flutter 中文社区(官方QQ群:338252156)共同成长,谢谢大家~
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/107656.html
摘要:运行一下,发现路由变了,当前项有标识了,子菜单的显示与隐藏也有了原理也很简单。 1.前言 上篇文章(webpack+vue项目实战(一,搭建运行环境和相关配置))搭建了好了基本的一个项目目录,安好好了一些要用到的依赖,以及把项目跑了起来。接下来,我们就进行第二步的操作,第二步就是做好一个开发系统的主页面,这个页面主要也就是一个侧边栏,通过侧边栏的各个选项来进行操作(切换各个组件)。比如...
摘要:清单一些说明注意文档的书写顺序,先写两侧栏,再写主面板,更换后则侧栏会被挤到下一列圣杯布局和双飞翼布局都会用到。可以通过设置的属性或使用双飞翼布局避免问题。双飞翼布局不用设置相对布局,以及对应的和值。 本文首发于知乎专栏:前端指南 CSS布局 布局是CSS中一个重要部分,本文总结了CSS布局中的常用技巧,包括常用的水平居中、垂直居中方法,以及单列布局、多列布局的多种实现方式(包括传统的...
摘要:有二维码扫描功能,还做了类似消息可拖拽效果,上拉下拉刷新,轮播图组件。特别适合用于基于模式的移动应用程序开发。简介是一个用基于,和的,创建移动跨平台移动应用程序的快速开发平台。 这个项目做得比较早,当时是基于ionic1和angular1做的。做了四个tabs的app,首页模仿携程首页,第二页主要是phonegap调用手机核心功能,第三页模仿微信和qq聊天页,第四页模仿一般手机的表单设...
摘要:当发送按钮触发事件后调用函数,在中执行了方法,此时根据中的变量变更重新渲染对象,然后大家就可以看到消息记录框中底部新增了一行消息。 熟悉了flutter的各种控件和相互嵌套的代码结构后,可以再加深一点难度:加入动画特效。 虽然flutter的内置Metarial控件已经封装好了符合其设计语言的动画特效,使开发者节约了不少视觉处理上的精力,比如点击或长按listTile控件时自带水波纹动...
阅读 1948·2021-11-22 19:20
阅读 2591·2021-11-22 13:54
阅读 1889·2021-09-04 16:40
阅读 1775·2021-08-13 11:54
阅读 2577·2019-08-30 15:55
阅读 3442·2019-08-29 13:51
阅读 503·2019-08-29 11:09
阅读 2976·2019-08-26 14:06