前言

上篇文章介绍了展示列表的构建. 片尾预告了这篇文章的内容, 主要包括列表的刷新,加载, 导航跳转详情web页面. 网络数据请求. 绑定数据. 效果图如下:

1.gif

网络请求

使用开源库dio: ^1.0.6进行的网络请求.简单易用.另附一个三方库查询地址地址. 方便查找需要用到的资源与轮子

pubspec.yaml上添加dio库.进行一下简单的封装, 新建一个apiUtils.dart文件, 如下

import 'package:dio/dio.dart';

import 'dart:async';

var dio = new Dio();

class ApiUtils {

static Future get(String url,{Map params}) async{

var response = await dio.get(url, data: params);

return response.data;

}

static Future post(String url,Map params) async{

var response = await dio.post(url, data: params);

return response.data;

}

}

然后在已入该文件进行网络请求

Future _getListData([Map params]) async {

// URL地址

const juejin_flutter =

'https://timeline-merger-ms.juejin.im/v1/get_tag_entry?src=web&tagId=5a96291f6fb9a0535b535438';

var pageIndex = (params is Map) ? params['pageIndex'] : 0;

// 参数

final _param = {'page': pageIndex, 'pageSize': 20, 'sort': 'rankIndex'};

// 返回结果

var response = await ApiUtils.get(juejin_flutter, params: _param);

var responseList = response['d']['entrylist'];

var pageTotal = response['d']['total'];

var pageSize = 20;

if (!(pageTotal is int) || pageTotal <= 0) {

pageTotal = 0;

}

pageIndex += 1;

List resultList = new List();

for (int i = 0; i < responseList.length; i++) {

try {

// json数据转化model

NewsModel cellData = new NewsModel.fromJson(responseList[i]);

resultList.add(cellData);

} catch (e) {

// No specified type, handles all

}

}

// 刷新页面

setState(() {

items.addAll(resultList);

});

// 自定义数据

Map result = {

"list": resultList,

'total': pageTotal,

'pageIndex': pageIndex,

'pageSize': pageSize,

};

return result;

}

其中NewsModel为数据model, 具体内容跟数据请求格式对应 代码如下:

class NewsModel {

bool hot;

String isCollection;

String tag;

String username;

int collectionCount;

int commentCount;

String title;

String detailUrl;

NewsModel(

{this.hot,

this.tag,

this.username,

this.collectionCount,

this.commentCount,

this.title,

this.detailUrl,

this.isCollection});

factory NewsModel.fromJson(Map json) {

String _tag = '';

if (json['tags'].length > 0) {

_tag = '${json['tags'][0]['title']}/';

}

return NewsModel(

hot: json['hot'],

collectionCount: json['collectionCount'],

commentCount: json['commentsCount'],

tag: '$_tag${json['category']['name']}',

username: json['user']['username'],

title: json['title'],

detailUrl: json['originalUrl'],

isCollection: json['type'],

);

}

}

上面代码可以基本完成了一个get方法的网络请求.下面请求下来数据, 就要进行数据绑定

数据绑定与列表刷新加载

一个app中会存在多个list, 这样只要我们封装好一个通用的list, 任何页面就可以使用. 论封装的重要性. 接下来创建一个commonLIst.dart文件作为通用list, 需要实现内容:

1, 实现刷新,请求数据

2, 加载提示,请求数据

3, 传入属性包括(返回的每行组件cell, 头部视图, 请求数据函数), 目前这些

参数设置

class CommonList extends StatefulWidget {

final renderItem;

final requestApi;

final headerView;

const CommonList([this.requestApi, this.renderItem, this.headerView])

: super();

@override

State createState() {

return new CommonListState();

}

}

返回的主要代码, 一看就懂, 我就不介绍了.

bool isLoading = false; // 是否正在请求数据中,

bool _hasMore = true; // 是否还有更多数据可加载

int _pageIndex = 0; // 页面的索引

int _pageTotal = 0; // 数据总数

int _pageSize = 20; // 页面数量

List items = new List();

ScrollController _scrollController = new ScrollController();

// 监听滑到最底部, 进行网络请求

@override

void initState() {

super.initState();

_getMoreData();

_scrollController.addListener(() {

// 如果下拉的当前位置到scroll的最下面

if (_scrollController.position.pixels ==

_scrollController.position.maxScrollExtent) {

_getMoreData();

}

});

}

@override

void dispose() {

super.dispose();

_scrollController.dispose();

}

Future _getMoreData() async {

// 如果加载数据loading为true,同时还有更多数据需要加载

if (!isLoading && _hasMore) {

setState(() {

isLoading = true;

});

List newEntries = await mokeHttpRequest();

_hasMore = ((_pageIndex + 1) * _pageSize <= _pageTotal);

// 状态加载完成执行

if (this.mounted) {

setState(() {

items.addAll(newEntries);

isLoading = false;

});

}

} else if (!isLoading && !_hasMore) {

}

}

// 数据请求

Future mokeHttpRequest() async {

if (widget.requestApi is Function) {

// 传入索引值

final listObj = await widget.requestApi({'pageIndex': _pageIndex});

// pageIndex, total, list. 网络请求返回值

_pageIndex = listObj['pageIndex'];

_pageTotal = listObj['total'];

_pageSize = listObj['pageSize'];

return listObj['list'];

} else {

// 如没有请求网络方法, 则延迟2s返回空数组

return Future.delayed(Duration(seconds: 2), () {

return [];

});

}

}

@override

Widget build(BuildContext context) {

// RefreshIndicator为实现刷新加载使用组件

return new RefreshIndicator(

child: ListView.builder(

itemCount: items.length + 1,

itemBuilder: (context, index) {

if (index == 0 && index != items.length && widget.headerView is Function) {

return widget.headerView();

}

if (index == items.length) {

return _buildProgressIndicator();

} else {

if (widget.renderItem is Function) {

// 将数据items[index]返回给item组件. 进行数据绑定

return widget.renderItem(items[index], index);

}

}

},

// 用于监控列表滑到底部

controller: _scrollController,

),

// 刷新函数

onRefresh: _handleRefresh,

color: Colors.green,

);

}

重点介绍一下方法里面没有实现的方法

_handleRefresh方法

Future _handleRefresh() async {

List newEntries = await mokeHttpRequest();

// this.mounted标识完成请求后更新数据

if (this.mounted) {

setState(() {

items.clear();

items.addAll(newEntries);

isLoading = false;

_hasMore = true;

});

}

}

_buildProgressIndicator方法显示刷新与加载的数据UI显示

// 加载中

Widget _buildLoadText() {

return Container(

child: Padding(

padding: const EdgeInsets.all(18.0),

child: Center(

child: Text("没有数据更多了!!!"),

),

));

}

Widget _buildProgressIndicator() {

if (_hasMore) {

return new Padding(

padding: const EdgeInsets.all(8.0),

child: new Center(

child: Column(

children: [

new Opacity(

opacity: isLoading ? 1.0 : 0.0,

child: new CircularProgressIndicator(

valueColor: AlwaysStoppedAnimation(Colors.green)),

),

SizedBox(height: 20.0),

Text(

'数据加载中...',

style: TextStyle(fontSize: 14.0),

)

],

)

//child:

),

);

} else {

return _buildLoadText();

}

}

以上一封装好了. 使用方法, 引入文件路径之后, 如下代用即可

new CommonList(参数, 参数, 参数)

头部视图数据目前是写死的数据,定义在页面中

// 附上地址

https://img.alicdn.com/tfs/TB1W4hMAwHqK1RjSZJnXXbNLpXa-519-260.jpg,

https://img.alicdn.com/tfs/TB1XmFIApzqK1RjSZSgXXcpAVXa-720-338.jpg',

https://img.alicdn.com/tfs/TB1mClCABLoK1RjSZFuXXXn0XXa-600-362.jpg,

https://img.alicdn.com/tfs/TB1fXxIAAvoK1RjSZFNXXcxMVXa-600-362.jpg

导航跳转

利用new GestureDetector()组件添加onTap方法, 实现跳转, 其中NewsDetails为新建页面将detailUrl传递进去, 展示webView页面

onTap: () {

Navigator.push(

context,

new MaterialPageRoute(builder: (context) => new NewsDetails(item.detailUrl, '详情')),

);

},

webView详情页面加载

引入webview_flutter: ^0.3.0第三方库, detailUrl为传入的属性之后代码如下:

import 'package:flutter/material.dart';

import 'package:webview_flutter/webview_flutter.dart';

class NewsDetails extends StatefulWidget{

final detailUrl;

final title;

const NewsDetails([this.detailUrl, this.title]) : super();

@override

State createState() {

return new NewsDetailsState();

}

}

class NewsDetailsState extends State{

@override

Widget build(BuildContext context){

return new Scaffold(

appBar: new AppBar(

title: new Text(widget.title),

backgroundColor: Colors.green

),

body: new Container(

child: new WebView(

initialUrl: widget.detailUrl,

),

),

);

}

}

最后

以上实现的内容为前言中图片显示内容. 总结一下包括, 网络请求与数据绑定,页面跳转,webView页面加载.

预告

1,搜索功能

2,封装一个错误页, 空数据页面

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐