上图是要实现的效果,废话不多说上代码。。由于涉及到公司的接口,所以代码里涉及这块就不贴出来了。
1 首先创建一个类 继承 StatelessWidget 由于是我的模块 就命名为MineLayout
class ReadLayout extends StatelessWidget { @override Widget build(BuildContext context) { Widget tabBarContainer = new TabBar(//创建TabBar实例 isScrollable: true, //这个属性是导航栏是否支持滚动,false则会挤在一起了 unselectedLabelColor: Colors.grey, //未选标签标签的颜色(这里定义为灰色) labelColor: Colors.black,//选中的颜色(黑色) indicatorColor: Colors.black,//指示器颜色 indicatorWeight: 2.0,//指示器厚度 tabs: choices.map((Choice choice) {//选项卡 return new Tab( text: choice.title, ); }).toList(), ); Widget tabContainer = new DefaultTabController( length: choices.length, initialIndex: 0, //初始索引 child: new Column( children: <Widget>[ new Container( constraints: new BoxConstraints.expand( height: Theme.of(context).textTheme.display1.fontSize * 1.1 + 50.0, ), padding: const EdgeInsets.all(8.0), color: Colors.teal.shade700, alignment: Alignment.center, child: tabBarContainer, ), new Expanded( child: new TabBarView( children: choices.map((Choice choice) { return new BookListView(categoryId: choice.categoryId);//一个属于展示内容的listview }).toList(), ), ), ], ), ); return tabContainer; } }
注意:上面我不是没有Appbar 的,所以默认会侵入到系统栏,所以我定义了高度
height: Theme.of(context).textTheme.display1.fontSize * 1.1 + 50.0,
接着定义一个类去封装顶部标签class Choice { const Choice({this.title, this.categoryId}); final String title;//这个参数是分类名称 final int categoryId;//这个适用于网络请求的参数,获取不同分类列表 } const List<Choice> choices = const <Choice>[ const Choice( title: '通话故事', categoryId: 1, ), const Choice( title: '科幻灵异', categoryId: 2, ), const Choice( title: '经典名著', categoryId: 3, ), const Choice( title: '科普百科', categoryId: 4, ), const Choice( title: '历史传记', categoryId: 5, ), const Choice( title: '诗歌散文', categoryId: 6, ), const Choice( title: '幽默成长', categoryId: 7, ), const Choice( title: '寓言传说', ), ];接着我们来看listview 的具体实现吧。
原理很简单,就是在初始化时加载用过异步的方式加载网络数据,将请求到的数据放到定义好的集合里面,然后调用setState方法重新构建widget。这时listview的itemcount就有数了。
class _BookListViewState extends State<BookListView> { List widgets = []; int bookId; String bookName; String coverKey; int picture; int media; int lang; int isQuestion; String author; String introduction; int downloadNum; String coverUrl; // final List<RowsBean> books; @override void initState() { // TODO: implement initState super.initState(); loadData(widget.categoryId); } @override Widget build(BuildContext context) { return new Scaffold( body: new ListView.builder( itemCount: widgets.length, itemBuilder: (BuildContext context, int position) { return getRow(position); })); } loadData(int categoryId) async { String dataURL = "你的API?categoryId=$categoryId"; http.Response response = await http.get(dataURL); print(response.body); Map data = JSON.decode(response.body); var retobj = data['retobj']; List rows = retobj['rows']; widgets = rows; setState(() { // JsonDecoder decoder = new JsonDecoder(); // // // 将给定的JSON字符串输入转换为其对应的对象 // List<List<RowsBean>> json = decoder.convert(response.body); // // 输出给定的JSON数据 // print(json[0][1]); // widgets = JSON.decode(response.body); }); } Widget getRow(int i) { return new BookListItem( bookId: widgets[i]['bookId'], bookName: widgets[i]['bookName'], coverKey: widgets[i]['coverKey'], picture: widgets[i]['picture'], media: widgets[i]['media'], lang: widgets[i]['lang'], isQuestion: widgets[i]['isQuestion'], introduction: widgets[i]['introduction'], downloadNum: widgets[i]['downloadNum'], author: widgets[i]['author'], coverUrl: widgets[i]['coverUrl'], ); } }
上面涉及到网络请求和json解析,楼主该开始也不知道怎么解析,官方的都是返回的一个list类型的json,看看我们返回的json类型吧
接着就需要写item的布局了,布局就是左边一张图,右边一个模块,右边模块采用线性布局。
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class BookListItem extends StatelessWidget { BookListItem( {this.bookId, this.bookName, this.coverKey, this.picture, this.media, this.lang, this.isQuestion, this.author, this.introduction, this.downloadNum, this.coverUrl}) : super(key: new ObjectKey(bookId)); int bookId; String bookName; String coverKey; int picture; int media; int lang; int isQuestion; String author; String introduction; int downloadNum; String coverUrl; @override Widget build(BuildContext context) { print('bookId:$bookId'); Widget titleSection = new Container( padding: const EdgeInsets.fromLTRB(12.0, 8.0, 0.0, 0.0), child: new Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ new Image.network( coverUrl, width: 107.0, height: 130.0, ), new Expanded( child: new Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ new Container( padding: const EdgeInsets.fromLTRB(6.0, 15.0, 24.0, 0.0), child: new Text( bookName, style: new TextStyle( fontWeight: FontWeight.bold, fontSize: 17.0), ), ), new Container( padding: const EdgeInsets.fromLTRB(6.0, 16.0, 24.0, 0.0), child: new Text( author, style: new TextStyle( fontWeight: FontWeight.bold, fontSize: 13.0), ), ), new Container( padding: const EdgeInsets.fromLTRB(6.0, 18.0, 24.0, 2.0), child: new Text( introduction, overflow: TextOverflow.ellipsis, textAlign: TextAlign.start, maxLines: 3, style: new TextStyle( fontSize: 12.0, fontStyle: FontStyle.normal), ), ), ], ), ), ], ), ); return titleSection; } }
ok, 本节就说道这里,文笔简陋,见谅,欢迎私信,互相学习。。