基于gank的api,做一个应用,包含普通应用用到的功能.
构建一个gank干货的列表. https://github.com/lijinshanmx/flutter_gank/ 这是他的官方应用.
首先列表的构建前面的翻译的文章已经有了.这里不多说了.先解析干货的数据:
使用的是http://gank.io/api/today这个地址.
返回的结构是:
{ "category": [ "App", "iOS", "拓展资源", "瞎推荐", "Android", "前端", "福利", "休息视频" ], "error": false, "results": { "Android": [ { "_id": "5bbb01af9d21226111b86f0d", "createdAt": "2018-10-08T07:05:19.297Z", "desc": "适用于Android的灵活,强大且轻量级的插件框架【爱奇艺】", "publishedAt": "2019-04-10T00:00:00.0Z", "source": "chrome", "type": "Android", "url": "https://github.com/iqiyi/Neptune", "used": true, "who": "潇湘剑雨" }], App:[ { "_id": "5771ce2b421aa931d274f244", "createdAt": "2016-06-28T09:08:59.622Z", "desc": "一款类似豆瓣读书的APP,提供一个书籍查看、搜索、交流的平台,数据来自豆瓣(爬虫),后端LeanCloud。", "images": [ "http://img.gank.io/0b7e425d-f61c-4eff-ae9c-8b5613020be9", "http://img.gank.io/c0cc0c8b-17b6-4321-bfdc-1cb0732edd4d", "http://img.gank.io/0203a555-edc1-4577-b83b-42a8a723dd87", "http://img.gank.io/8671a3f2-5546-4f0b-9d5c-538ad51aa8cf" ], "publishedAt": "2019-08-06T11:58:37.715Z", "source": "web", "type": "App", "url": "https://github.com/Blankeer/SoleBooks", "used": true, "who": "潇湘剑雨" }]
建两个 类,GankToday和GankBean.
class GankBean { String id; String createdAt; String desc; String publishedAt; String source; String type; String url; bool used; String who; List<String> images; GankBean.fromJson(Map<String, dynamic> json) { //print("item:$json"); id = json['_id']; createdAt = json['createdAt']; desc = json['desc']; publishedAt = json['publishedAt']; source = json['source']; type = json['type']; url = json['url']; used = json['used']; desc = json['desc']; who = json['who']; images = json['images']?.map<String>((image) => image as String)?.toList() ?? []; } }
class GankToday { bool error; List<String> category; Map<String, List<GankBean>> items = Map(); List<GankBean> beans = []; GankToday(this.category); GankToday.fromJson(Map<String, dynamic> json) { error = json['error']; category = List<String>.from(json['category']); //print('json:' + category.toString()); var results = json['results']; results.forEach((key, listVal) { if (key != '福利') { items[key] = _parseGankBeanFromJson(key, listVal); } }); print("decode end"); } List<GankBean> _parseGankBeanFromJson(key, listVal) { var list = listVal.map<GankBean>((item) => GankBean.fromJson(item)).toList(); beans.addAll(list); return list; }
这种数据结构,用序列化的方式似乎不太可行,所以只能手动了. 其间参考了一下官方的应用, 简化了一点代码.
构建页面:
class GankJsonListPage extends StatefulWidget { GankJsonListPage({Key key, this.title}) : super(key: key); final String title; @override _GankJsonListPageState createState() => new _GankJsonListPageState(); }
这都 是标准的;
class _GankJsonListPageState extends State<GankJsonListPage> {
ListView buildListView() => ListView.builder( itemCount: length, itemBuilder: (BuildContext context, int position) { return buildRow(position); }); Widget buildRow(int i) { var beans = gankToday.beans; //print('bean i:$i data:$beans'); if (beans == null) { return Text("no items:"); } var bean = beans[i]; if (bean.images == null || bean.images.length < 1) { return GestureDetector( onTap: () { setState(() { detail(bean); }); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.only( left: 10.0, right: 10.0, top: 5.0, bottom: 5.0), child: Text("Title:${bean.publishedAt}")), Padding( padding: EdgeInsets.only( left: 10.0, right: 10.0, top: 5.0, bottom: 5.0), child: Text("Title:${bean.desc}")), ], ), ); } else { return GestureDetector( onTap: () { setState(() { detail(bean); }); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.only( left: 10.0, right: 10.0, top: 5.0, bottom: 5.0), child: Text("Title:${bean.publishedAt}")), Padding( padding: EdgeInsets.only( left: 10.0, right: 10.0, top: 5.0, bottom: 5.0), child: Text("Url:${bean.images[0]}")), Padding( padding: EdgeInsets.only(left: 10.0, right: 10.0), child: Image( image: CachedNetworkImageProvider(bean.images[0]), //width: album.images.width.toDouble(), //height: album.images.height.toDouble(), fit: BoxFit.fitWidth, ), ), ], ), ); } } }
上面,分有图与无图的,先这么用, 后续可以按多个类型来处理,重构一下.
跳转到详情页:
void detail(GankBean gankBean) { Navigator.of(context).push( new MaterialPageRoute<void>( builder: (BuildContext context) { return new GankDetailPage( gankBean: gankBean, ); }, ), ); }
@override Widget build(BuildContext context) { return Scaffold( body: buildListView(), ); }
到这里widget就构建完成了.
到加载数据了:
GankToday gankToday = GankToday([]); var length = 0; @override void initState() { super.initState(); loadData(); }
loadData() async { String dataURL = "http://gank.io/api/today"; http.Response response = await http.get(dataURL); setState(() { Map<String, dynamic> decodeJson = json.decode(response.body); gankToday = GankToday.fromJson(decodeJson); length = gankToday.items[gankToday.category[0]].length; print( "length:${gankToday.category}, content:${gankToday.items.length}"); }); //如果你把json文件放在assets目录里,也是可以的,这样就调用下面这样的代码. /*loadAsset().then((value) { setState(() { var raw = json.decode(value); //it's a Map<>,translate to List:raw.map().toList() albums = new List<Album>.from(raw.map((i) => Album.fromJson(i)).toList()); print(albums[0]); }); });*/ }
然后在main里调用就可以了.
runApp(HomeTabsPage());
class HomeTabsPage extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Home', theme: new ThemeData( primarySwatch: Colors.blue, //primaryColor: Colors.white, ), home: new GankJsonListPage(), ); } }
到此,一个简单的列表就完成了.代码就是有点乱.到详情页的部分还没有添加.可以自行完成.