写点什么

Flutter 动手实战,大佬手把手教你如何仿写出大厂的 APP,Android 软件开发面试题

用户头像
Android架构
关注
发布于: 刚刚

│ ├── grid_nav_model.dart│ ├── home_model.dart│ ├── sales_box_model.dart│ ├── seach_model.dart*│ ├── travel_hot_keyword_model.dart│ ├── travel_model.dart*│ ├── travel_params_model.dart*│ ├── travel_search_hot_model.dart│ ├── travel_search_model.dart│ └── travel_tab_model.dart├── navigator/│ └── tab_navigater.dart├── pages/│ ├── destination_page.dart│ ├── destination_search_page.dart│ ├── home_page.dart│ ├── my_page.dart│ ├── search_page.dart│ ├── speak_page.dart*│ ├── test_page.dart│ ├── travel_page.dart│ ├── travel_search_page.dart│ └── travel_tab_page.dart*├── plugin/│ ├── asr_manager.dart*│ ├── side_page_view.dart│ ├── square_swiper_pagination.dart│ └── vertical_tab_view.dart├── util/│ └── navigator_util.dart*└── widget/├── grid_nav.dart├── grid_nav_new.dart├── loading_container.dart├── local_nav.dart├── sales_box.dart├── scalable_box.dart├── search_bar.dart*├── sub_nav.dart└── webview.dart


整个项目就是以上这些文件了 (具体的就不一个一个分析了,如,感兴趣,大家可以 clone 源码运行起来,自然就清除了)

项目功能详细概述(所用知识点)

首先,来看看首页功能及所用知识点,首页重点看下以下功能实现:


  • 渐隐渐现的 appBbar

  • 搜索组件的封装

  • 语音搜索页面

  • banner 组件

  • 浮动的 icon 导航

  • 渐变不规则带有背景图的网格导航

渐隐渐现的 appBbar

先来看看具体效果,一睹芳容,如图:



滚动的时候 appBar 背景色从透明变成白色或白色变成透明,这里主要用了 flutterNotificationListener 组件,它会去监听组件树冒泡事件,当被它包裹的的组件*(子组件)* 发生变化时,Notification 回调函数会被触发,所以,通过它可以去监听页面的滚动,来动态改变 appBar 的透明度*(alpha)*,代码如下:


NotificationListener(onNotification: (scrollNotification) {if (scrollNotification is ScrollUpdateNotification &&scrollNotification.depth == 0) {_onScroll(scrollNotification.metrics.pixels);}return true;},child: ...


Tips:scrollNotification.depth 的值 0 表示其子组件*(只监听子组件,不监听孙组件)*;scrollNotification is ScrollUpdateNotification 来判断组件是否已更新,ScrollUpdateNotification 是 notifications 的生命周期一种情况,分别有一下几种:


  • ScrollStartNotification 组件开始滚动

  • ScrollUpdateNotification 组件位置已经发生改变

  • ScrollEndNotification 组件停止滚动

  • UserScrollNotification 不清楚


这里,我们不探究太深入,如想了解可多查看源码。


_onScroll 方法代码如下:


void _onScroll(offset) {double alpha = offset / APPBAR_SCROLL_OFFSET; // APPBAR_SCROLL_OFFSET 常量,值:100;offset 滚动的距离


//把 alpha 值控制值 0-1 之间 if (alpha < 0) {alpha = 0;} else if (alpha > 1) {alpha = 1;}setState(() {appBarAlpha = alpha;});print(alpha);}

搜索组件的封装

搜索组件效果如图:



以下是首页调用 searchBar 的代码:


SearchBar(searchBarType: appBarAlpha > 0.2 //searchBar 的类:暗色、亮色? SearchBarType.homeLight: SearchBarType.home,inputBoxClick: _jumpToSearch, //点击回调函数 defaultText: SEARCH_BAR_DEFAULT_TEXT, // 提示文字 leftButtonClick: () {}, //左边边按钮点击回调函数 speakClick: _jumpToSpeak, //点击话筒回调函数 rightButtonClick: _jumpToUser, //右边边按钮点击回调函数),


其实就是用 TextField 组件,再加一些样式,需要注意点是:onChanged,他是 TextField 用来监听文本框是否变化,通过它我们来监听用户输入,来请求接口数据;具体的实现细节,请查阅源码: 点击查看searchBar源码

语音搜索页面

语音搜索页面效果如图:由于模拟器无法录音,所以无法展示正常流程,如果录音识别成功后会返回搜索页面,在项目预览视频中可以看到正常流程。


[图片上传失败...(image-190ee2-1589860267422)]


语音搜索功能使用的是百度的语言识别 SDK,原生接入之后,通过 MethodChannel 和原生 Native 端通信,这里不做重点讲述(这里会涉及原生 Native 的知识)。


重点看看点击录音按钮时的动画实现,这个动画用了 AnimatedWidget 实现的,代码如下:


class AnimatedWear extends AnimatedWidget {final bool isStart;static final _opacityTween = Tween<double>(begin: 0.5, end: 0); // 设置透明度变化值 static final _sizeTween = Tween<double>(begin: 90, end: 260); // 设置圆形线的扩散值


AnimatedWear({Key key, this.isStart, Animation<double> animation}): super(key: key, listenable: animation);


@overrideWidget build(BuildContext context) {final Animation<double> animation = listenable; // listenable 继承 AnimatedWidget,其实就是控制器,会自动监听组件的变化 return Container(height: 90,width: 90,child: Stack(overflow: Overflow.visible,alignment: Alignment.center,children: <Widget>[...// 扩散的圆线,其实就是用一个圆实现的,设置圆为透明,设置 borderPositioned(left: -((_sizeTween.evaluate(animation) - 90) / 2), // 根据 _sizeTween 动态设置 left 偏移值 top: -((_sizeTween.evaluate(animation) - 90) / 2), // 根据 _sizeTween 动态设置 top 偏移值 child: Opacity(opacity: _opacityTween.evaluate(animation), // 根据 _opacityTween 动态设置透明值 child: Container(width: isStart ? _sizeTween.evaluate(animation) : 0, // 设置 宽 height: _sizeTween.evaluate(animation), // 设置 高 decoration: BoxDecoration(color: Colors.transparent,borderRadius: BorderRadius.circular(_sizeTween.evaluate(animation) / 2),border: Border.all(color: Color(0xa8000000),)),),),),],),);}}


其他细节,如:点击时提示录音,录音失败提示,点击录音按钮出现半透明黑色圆边框,停止后消失等,请查看源码

banner 组件

效果如图:



banner使用的是 flutter 的 flutter_swiper 插件实现的,代码如下:


Swiper(itemCount: bannerList.length, // 滚动图片的数量 autoplay: true, // 自动播放 pagination: SwiperPagination( // 指示器 builder: SquareSwiperPagination(size: 6, // 指示器的大小 activeSize: 6, // 激活状态指示器的大小 color: Colors.white.withAlpha(80), // 颜色 activeColor: Colors.white, // 激活状态的颜色),alignment: Alignment.bottomRight, // 对齐方式 margin: EdgeInsets.fromLTRB(0, 0, 14, 28), // 边距),itemBuilder: (BuildContext context, int index) { // 构造器 return GestureDetector(onTap: () {CommonModel model = bannerList[index];Navigator.push(context,MaterialPageRoute(builder: (context) => WebView(url: model.url,),),);},child: Image.network(bannerList[index].icon,fit: BoxFit.fill,),);},),


具体使用方法,可以去 flutter 的官方插件库 pub.dev 查看:点击flutter_swiper查看


Tips:需要注意的是,我稍改造了一下指示器的样式,flutter_swiper 只提供了 3 种指示器样式,如下:


  • dots = const DotSwiperPaginationBuilder(),圆形

  • fraction = const FractionPaginationBuilder(),百分数类型的,如:1/6,表示 6 页的第一页

  • rect = const RectSwiperPaginationBuilder(),矩形


并没有上图的激活状态的长椭圆形,其实就是按葫芦画瓢,自己实现一个长椭圆类型,如知详情,可点击查看长椭圆形指示器源码

浮动的 icon 导航

icon 导航效果如图:



icon 导航浮动在 banner 之上,其实用的是 flutterStack 组件,Stack 组件能让其子组件堆叠显示,它通常和 Positioned 组件配合使用,布局结构代码如下:


ListView(children: <Widget>[Container(child: Stack(children: <Widget>[Container( ... ), //这里放的是 banner 的代码 Positioned( ... ), //这个就是 icon 导航,通过 Positioned 固定显示位置],),),Container( ... ), // 这里放的网格导航及其他],),

渐变不规则带有背景图的网格导航

网格导航效果如图:



如图,网格导航分为三行四栏,而第一行分为三栏,每一行的第一栏宽度大于其余三栏,其余三栏均等,每一行都有渐变色,而且第一、二栏都有背景图;flutterColumn 组件能让子组件竖轴排列, Row 组件能让子组件横轴排列,布局代码如下:


Column( // 最外面放在 Column 组件 children: <Widget>[Container( // 第一行包裹 Container 设置其渐变色 height: 72,decoration: BoxDecoration(gradient: LinearGradient(colors: [ //设置渐变色 Color(0xfffa5956),Color(0xffef9c76).withAlpha(45)]),),child: Row( ... ), // 第一行),Padding(padding: EdgeInsets.only(top: 1), // 设置行直接的间隔),Container(height: 72,decoration: BoxDecoration(gradient: LinearGradient(colors: [ //设置渐变色 Color(0xff4b8fed),Color(0xff53bced),]),),child: Row( ... ), // 第二行),Padding(padding: EdgeInsets.only(top: 1), // 设置行直接的间隔),Container(height: 72,decoration: BoxDecoration(gradient: LinearGradient(colors: [ //设置渐变色 Color(0xff34c2aa),Color(0xff6cd557),]),),child: Row( ... ), // 第三行),],),


其实,具体实现的细节还是很多的,比如:


  • 怎么设置第一栏宽度偏大,其他均等;

  • 第一行最后一栏宽度是其他的 2 倍;

  • 第一、二栏的别截图及浮动的红色气泡 tip 等;


在这里就不细讲,否则篇幅太长,如想了解详情 点击查看源码


其次,再来看看目的地页面功能及所用知识点,重点看下以下功能实现:


  • 左右布局 tabBarListView

  • 目的地搜索页面

左右布局 tabBarListView

具体效果如图:点击左边标签可以切换页面,左右滑动也可切换页面,点击展开显示更多等



其实官方已经提供了 tabBarTabBarView 组件可以实现上下布局的效果*(旅拍页面就是用这个实现的)*,但是它无法实现左右布局,而且不太灵活,所以,我使用的是 vertical_tabs插件, 代码如下:


VerticalTabView(tabsWidth: 88,tabsElevation: 0,indicatorWidth: 0,selectedTabBackgroundColor: Colors.white,backgroundColor: Colors.white,tabTextStyle: TextStyle(height: 60,color: Color(0xff333333),),tabs: tabs,contents: tabPages,),),


具体使用方法,在这里就不赘述了,点击vertical_tabs查看


Tips:这里需要注意的是:展开显示更多 span 标签组件的实现,因为,这个组件在很多的其他组件里用到而且要根据接口数据动态渲染,且组件自身存在状态的变化,这种情况下,最好是把他单独封装成一个组件*(widget)*,否则,很难控制自身状态的变化,出现点击没有效果,或点击影响其他组件。

目的地搜索页面

效果如图:点击搜索结果,如:点击‘一日游‘,会搜索到‘一日游‘的相关数据



目的地搜索页面,大多都是和布局和对接接口的代码,在这里就不再赘述。


然后就是旅拍页面功能及所用知识点,重点看下以下功能实现:


  • 左右布局 tabBarListView

  • 瀑布流卡片

  • 旅拍搜索页

左右布局 tabBarListView

效果如图:可左右滑动切换页面,上拉加载更多,下拉刷新等



这个是flutter 提供的组件,tabBarTabBarView,代码如下:


Container(color: Colors.white,paddi


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


ng: EdgeInsets.only(left: 2),child: TabBar(controller: _controller,isScrollable: true,labelColor: Colors.black,labelPadding: EdgeInsets.fromLTRB(8, 6, 8, 0),indicatorColor: Color(0xff2FCFBB),indicatorPadding: EdgeInsets.all(6),indicatorSize: TabBarIndicatorSize.label,indicatorWeight: 2.2,labelStyle: TextStyle(fontSize: 18),unselectedLabelStyle: TextStyle(fontSize: 15),

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Flutter动手实战,大佬手把手教你如何仿写出大厂的APP,Android软件开发面试题