写点什么

Flutter 实战详解 -- 高仿好奇心日报,kotlin 核心编程

用户头像
Android架构
关注
发布于: 50 分钟前

dependencies: json_annotation: ^2.0.0 dev_dependencies: build_runner: ^1.0.0 json_serializable: ^2.0.0


创建一个 model.dart 文件 引入文件


import 'package:json_annotation/json_annotation.dart'; part 'model.g.dart';


其中这个 model.g.dart 等会儿会自动生成.这里需要掌握两个知识点


1.@JsonSerializable() 这是表示告诉编译器这个类是需要生成 Model 类的 2,@JsonKey 由于服务器返回的部分数据名称在 Dart 语言中是不被允许的,比如 has_more,Dart 中命名不能出现下划线,所以就需要用到 @JsonKey 来告诉编译器这个参数对于 json 中的哪个字段


@JsonSerializable()class Feed {String image;int type;@JsonKey(name: 'index_type')int indexType;Post post;@JsonKey(name: 'news_list')List<News> newsList;Feed(this.image,this.type,this.post,this.indexType,this.newsList);factory Feed.fromJson(Map<String,dynamic> json) => _FeedFromJson(json);Map<String, dynamic> toJson() => _FeedToJson(this);}


好了,写完后会报错,因为FeedFromJsonFeedToJson没有找到,这个时候在控制到输入flutter packages pub run build_runner build指令后会自动生成一个moded.g.dart文件,于是在网络请求下来数据后就可以用Feed feed = Feed.fromJson(data)这个方法来将 Json 中数据转换保存在 Feed 这个实例中了.在 model 类中还有些复杂的 Json 嵌套,但是也都很简单,大家看一眼应该就会了,哈哈.[JSON 和序列化具体教程](


)

5.轮播图

Flutter 中的轮播图我用到了[Flutter_Swiper](


)这个组件,这里设置小圆点属性的时候稍微麻烦了点,网上好像也没有讲到,我这里讲一下. 首先要创建 DotSwiperPaginationBuilder


DotSwiperPaginationBuilder builder = DotSwiperPaginationBuilder(color: Colors.white,//未选中圆点颜色 activeColor: Colors.yellow,//选中圆点颜色 size:7,//未选中大小 activeSize: 7,//选中圆点大小 space: 5//圆点间距);


然后在 Swiper 中的 pagination 属性中设置它


pagination: new SwiperPagination(builder: builder,),


  1. 网络请求 首先,展示页面要继承自StatefulWidget,因为需要动态更新数据和列表. 网络请求插件我用的[Dio](


),非常好用. 在initState方法中请求数据表示刚加载页面的时候进行网络请求,请求数据方法如下


void getData()async{if (lastKey == '0'){dataList = [];//下拉刷新的时候将 DataList 制空}Dio dio = new Dio();Response response = await dio.get("lastKey.json");Reslut reslut = Reslut.fromJson(response.data);if(!reslut.response.hasMore){return;//如果没有数据就不继续了}if(reslut.response.columns != null) {columnList = reslut.response.columns;}lastKey = reslut.response.lastKey;//更新 lastkeysetState(() {if (reslut.response.banners != null){banners = reslut.response.banners;//给轮播图赋值}dataList.addAll(reslut.response.feeds);//给数据源赋值});}


因为用到了 setState()方法,所以在该方法中改变了的数据会对其相应的地方进行刷新,比如设置了 ListView 的 itemCount 个数为 dataList.length,如果在 SetState 方法中 dataList.length 改变了,那么 ListView 的 itemCount 树也会自动改变并刷新 ListView.

7. 上拉刷新与加载

Flutter 中有RefreshIndicator用于下拉刷新,它有个onRefresh闭包方法,表示下拉的时候执行的方法,一般用于网络请求.onRefresh 方法如下


Future<void> handleRefresh() {final Completer<void> completer = Completer<void>();Timer(const Duration(seconds: 1), () {completer.complete();});return completer.future.then<void>(() {lastKey = '0';getData();});}


下拉加载的话需要初始化一个ScrollController,将它设为 ListView 的 controller,并对其进行监听,当滑动到最底部的时候进行网络请求.


@overridevoid initState() {url = widget.url;getData();_scrollController.addListener(() {///判断当前滑动位置是不是到达底部,触发加载更多回调 if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {getData();}});}final ScrollController _scrollController = new ScrollController();


上拉加载 loading 框用到了[flutter_spinkit](


)插件,提供了大量的加载样式.



代码如下


///上拉加载更多 Widget _buildProgressIndicator() {///是否需要显示上拉加载更多的 loadingWidget bottomWidget = new


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


Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[///loading 框 new SpinKitThreeBounce(color: Color(0xFF24292E)),new Container(width: 5.0,),]);return new Padding(padding: const EdgeInsets.all(20.0),child: new Center(child: bottomWidget,),);}

8. ListView 赋值

由于最上面有一个轮播图,最下面有加载框,所以 ListView 的 itemCount 个数为 dataList.length+2,又因为每个 item 之间都有一个浅灰色的风格线,所以需要用到ListView.separated,具体代码如下:


Widget build(BuildContext context) {return RefreshIndicator(onRefresh:(()=> _handleRefresh()),color: Colors.yellow,//刷新控件的颜色 child: ListView.separated(physics: const AlwaysScrollableScrollPhysics(),itemCount: _getListCount(),//item 个数 controller: _scrollController,//用于监听是否滑到最底部 itemBuilder: (context,index){if(index == 0){return SwiperWidget(context, banners);//如果是第一个,则展示 banner}else if(index < dataList.length + 1){return WidgetUtils.GetListWidget(context, dataList[index - 1]);//展示数据}else {return _buildProgressIndicator();//展示加载 loading 框}},separatorBuilder: (context,idx){//分割线 return Container(height: 5,color: Color.fromARGB(50,183, 187, 197),);},),);}

9. ListView 嵌套横向滑动 ListView

这种的话也稍微复杂一点,有两种样式.并且到滑到最右边的时候可以继续请求并加载数据.




首先来分析一下数据



这个 colunmns 就是横向滑动列表的重要数据.



里面的 id 是请求参数,show_type 表示列表的样式,location 表示插入的位置.而且通过抓取接口发现,当横向列表快要展示出来的时候,才会去请求横向列表的具体接口. 那么思路就很清晰了,在请求获得数据后遍历 colunmns,根据每个 colunmn 的 location 插入一个 Map,如下


data.insert(colunm.location, {'id':colunm.id,'showType':colunm.showType});


,再创建一个ColumnsListWidget类,继承自 StatefulWidget,是一个新 item,在滑动到该列表的位置的时候,会将该 Map 数据传给ColumnsListWidget,这个时候ColumnsListWidget就会加载数据并展示出来了,滑到最右边的时候加载和滑到最底部加载的方法一样,就不多说了.具体可以查看源码,关键代码如下:


static Widget GetListWidget(BuildContext context, dynamic data) {Widget widget;if(data.runtimeType == Feed) {if (data.indexType != null) {widget = NewsListWidget(context, data);} else if (data.type == 2) {widget = ListImageTop(context, data);} else if (data.type == 0) {widget = ActivityWidget(context, data);} else if (data.type == 1) {widget = ListImageRight(context, data);}}else{widget = ColumnsListWidget(id: data['id'],showType: data['showType'],);}


1.横向 ListView 外需要用Flexible包裹,Flexible 组件可以使 Row、Column、Flex 等子组件在主轴方向有填充可用空间的能力(例如,Row 在水平方向,Column 在垂直方向),但是它与[Expanded](


)组件不同,它不强制子组件填充可用空间。 2.ListView 初始位置用到padding: new EdgeInsets.symmetric(horizontal: 12.0),用padding: EdgeInsets.only(left: 12)的话会让 ListView 和最左边一直有条线

10.webview 加载复杂的 Html 字段


获取到网页详情的数据发现是 Html 字段,并且其中的 css 是 url 地址,试了很多 Flutter 加载 Html 的插件发现样式都不正确,最后决定使用原生和 Flutter 混编,这时候发现[flutter_webview_plugin](


)这个插件是使用原生网页的,不过它只支持加载 url,于是就需要做一些修改.


  • iOS 在FlutterWebviewPlugin.m文件中的- (void)navigate:(FlutterMethodCall*)call方法中的最后一排,将[self.webview loadRequest:request]方法改为[self.webview loadHTMLString:url baseURL:nil]

  • Android 在WebViewManager.java文件中webView.loadUrl(url)方法改为webView.loadData(url, "text/html", "UTF-8"),以及下面那排的void reloadUrl(String url) { webView.loadUrl(url); }改为void reloadUrl(String url) { webView.loadData(url, "text/html", "UTF-8"); } 由于服务器端返回的 Html 中的 css 和 js 文件地址是/assets/app3开头的,所以需要替换成绝对路径,所以要用到这个方法htmlBody.replaceAll( '/assets/app3','http://app3.qdaily.com/assets/app3') 好了,这下就可以呈现出漂亮的网页了.

11.ListView 嵌套 GridView

在点击横向滑动列表的总标题的时候,会进入到相关栏目的详情页,如图



这个 ListView 包含上下两部分.上面这部分为:



结构如下



下面就是一个 GridView,不过有时候下面会是 ListView,根据 showType 字段来判断,GridView 的代码如下:

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Flutter实战详解--高仿好奇心日报,kotlin核心编程