这里踩了一脚坑,写flutter的时候还是与js有差异的,这里主要涉及到几个知识点,看官需要对几部分内容先理解才容易处理,PS:其实现在我也还有点懵~
1. 网络请求,这里我分别使用了dio和http两个库
2. dart的json解析,不得不说,这个真神奇~
3 listView渲染及嵌套listView
4 FutureBuilder使用
吐槽:
坑1: dart对json的处理其实真的不方便,不能像js那样拿来就用,起初也找了一些json转换的插件,但是用着也都很不爽,一直有各种错误抛出来,最后决定还是自己写model解析json;
坑2: listView的嵌套主要注意一下子listView需要多设置两个属性就可以了,应该是为了解决滑动和计算子项高度的。
坑3:异步渲染还是使用FutureBuilder吧,这东西简单好用,最初我像js那样建一个全局变量,然后在http请求之后,使用setstate去赋值,看着逻辑很对,但是一直在第一次渲染的时候报空指针,我猜是还没有返回数据就渲染ui了,但是一直没解决掉,也可能是那个时候json解析还不太对,后来使用了这个FutureBuilder,从此没有烦恼了~
特别深奥的原理我还解释不清,只记录一下踩坑的点,大家多注意即可
下面是代码,欢迎指正,我分别给出我的dio请求库,和此处功能实现的代码
1 dio请求库
import 'package:dio/dio.dart';
import 'dart:async';
class DioUtil{
/// global dio object
static Dio dio;
/// default options
static const String API_PREFIX = 'http://10.0.3.2:8086/ylr-admin';
static const int CONNECT_TIMEOUT = 10000;
static const int RECEIVE_TIMEOUT = 3000;
/// http request methods
static const String GET = 'get';
static const String POST = 'post';
static const String PUT = 'put';
static const String PATCH = 'patch';
static const String DELETE = 'delete';
/// request method
static Future
我也是参考其他人的写法,拿来用的,这里说明一下API_PREFIX ,我用的genymotion模拟器 ,这个东西和本地交互的ip是10.0.3.2
2 功能实现
import 'dart:convert';
import 'package:date_format/date_format.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:ylr_app/base/base_detail.dart';
import 'package:ylr_app/constants/constants.dart';
import 'package:ylr_app/utils/dio_util.dart';
import 'package:ylr_app/widget/home_news_more.dart';
import 'package:http/http.dart' as http;
/// 资讯
class HomeNews extends StatefulWidget {
@override
_HomeNewsState createState() => _HomeNewsState();
}
class _HomeNewsState extends State {
// http
Future fetchPost() async {
final response =
await http.get(Constants.BASE_URL + '/api/article/getArticleList');
Utf8Decoder utf8decoder = Utf8Decoder(); //fix 中文乱码
var result = json.decode(utf8decoder.convert(response.bodyBytes));
return NewsModel.fromJson(result);
}
// dio
Future _getDataList() async {
var result = await DioUtil.request('/api/article/getArticleList',
method: DioUtil.GET);
return NewsModel.fromJson(result);
}
Widget _listItemBuilder(item) {
return GestureDetector(
child: Container(
margin: EdgeInsets.only(
top: 10,
),
height: ScreenUtil().setHeight(60),
child: Flex(
direction: Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 1,
child: Text('${item.title}',overflow: TextOverflow.ellipsis,),
),
Container(
alignment: Alignment.centerRight,
width: ScreenUtil().setWidth(260),
child: item.updateTime != null ?
Text(formatDate(DateTime.parse('${item.updateTime}'), [yyyy, '-', mm, '-', dd],)):
Text(formatDate(DateTime.parse('${item.createTime}'), [yyyy, '-', mm, '-', dd],)),
),
],
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BaseDetail(
contentTitle: '${item.title}',
isShowVideo: false,
id: int.parse('${item.id}'),
),
),
);
},
);
}
Widget _createListView(BuildContext context, AsyncSnapshot snapshot){
if(snapshot.hasData){
//数据处理
var data = snapshot.data;
List listData = (data.result as List).cast();
return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
NewsItemModel item = listData[index];
return _listItemBuilder(item);
},
itemCount: listData.length,
);
}
}
Widget _buildFuture(BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('还没开始网络请求');
case ConnectionState.active:
return Text('ConnectionState.active');
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
if (snapshot.hasError) return Text('Error: ${snapshot.error}');
return _createListView(context, snapshot);
default:
return null;
}
}
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(bottom: 5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
border: Border.all(
color: Color(Constants.border),
),
),
child: Padding(
padding: EdgeInsets.all(10),
child: Column(
children: [
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/listbar.png'),
fit: BoxFit.fitWidth,
),
),
child: Row(
children: [
Container(
width: ScreenUtil().setWidth(360),
margin: EdgeInsets.only(left: 10),
child: Text(
Constants.MODULE_HOME_NEWS,
style: TextStyle(
fontSize: Constants.font40,
),
),
),
Expanded(
flex: 1,
child: GestureDetector(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
Constants.MORE,
style: TextStyle(
fontSize: Constants.font30,
),
),
Icon(
Icons.arrow_forward_ios,
color: Colors.grey,
size: Constants.font36,
),
],
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomeNewsMore(),
),
);
},
),
),
],
),
),
FutureBuilder(
builder: _buildFuture,
future: _getDataList(),
),
],
),
),
);
}
}
// dart model
class NewsModel {
int code;
int timestamp;
String message;
bool success;
List result;
NewsModel(
{this.code, this.timestamp, this.message, this.success, this.result});
factory NewsModel.fromJson(Map json) {
final originList = json['result'] as List;
List newsItemModelList =
originList.map((value) => NewsItemModel.fromJson(value)).toList();
return NewsModel(
code: json['code'],
timestamp: json['timestamp'],
message: json['message'],
success: json['success'],
result: newsItemModelList);
}
}
class NewsItemModel {
int id;
String title;
String createTime;
String updateTime;
NewsItemModel({this.id, this.title, this.createTime, this.updateTime});
factory NewsItemModel.fromJson(Map json) {
return NewsItemModel(
id: json['id'],
title: json['title'],
createTime: json['createTime'],
updateTime: json['updateTime']);
}
}
这里给出了两种http请求库的使用方法,以及它们分别如何与自定义的dart model转换,最终生成列表是使用listView做的,原本的想法,是用for循环数据List渲染UI,也是一直报各种问题,最终都使用flutter提供的组件实现了,我想应该是会有其他办法实现,但是我对dart的语法还是不够熟悉,现阶段只能这么做了,以后有机会再试试其他写法,有知道的朋友路过,欢迎指教,提供给我一下事例代码,我就瞟一眼~