它分为首页,问答,视频和我的四大模块
创建lib/home/home.dart首页文件,使用的是bottomNavigationBar组件,官网也有介绍
它有一个onTap函数,这个函数会有一个index下标参数,同时准备四个模块页面news.dart,question.dart,video.dart和user.dart;里面的切换过程:定义上面四个页面数组,在body属性中将动态下标传递过去,在事件里更改这个下标即可,同时加上currentIndex动态属性点击效果,注意的是要用StatefulWidget组件.
在news/news.dart文件中
顶部的搜索是写入的样式,实际点击是进入另一个页面的,具体实现步骤:在AppBar组件的title中给一个自定义SearchBox组件,在SearchBox.dart组件中写我们的搜索内容:
使用的是DefaultTabController组件进行重新包装,里面放我们的Scaffold组件,具体如下:
import 'package:flutter/material.dart';
class TabBarBtn extends StatelessWidget {
final List channel;
TabBarBtn(this.channel);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: TabBar(
labelColor: Colors.black,
unselectedLabelColor: Colors.black45,
labelStyle: TextStyle(
fontSize: 14.0
),
indicatorColor: Colors.blueAccent,
indicatorWeight: 3.0,
indicatorSize: TabBarIndicatorSize.label,
labelPadding: EdgeInsets.symmetric(horizontal: 20.0),
isScrollable: true,
tabs: channel.map((value){
return Tab(text: value['name'],);
}).toList()
),
);
}
}
news.dart
class News extends StatefulWidget {
@override
_NewsState createState() => _NewsState();
}
class _NewsState extends State<News> {
List channels = [];
_getChannels () async{
print('关闭之后刷新复机');
var data = await PubMoudle.httpRequest('get', '/getchannels');
// print(data.data['data']['channels']);
setState(() {
channels = data.data['data']['channels'];
});
}
@override
void initState() {
super.initState();
_getChannels();
}
@override
Widget build(BuildContext context) {
return channels.length == 0 ?SizedBox():DefaultTabController(
length: channels.length,
child: Scaffold(
appBar: AppBar(
title: SearchBox(),
elevation: 0.0,
bottom: PreferredSize(
preferredSize: Size.fromHeight(50.0),
child: TabBarBtn(channels)
)
),
body: TabBarView(
children: channels.map((value){
return TabBarContent(value['id']);
}).toList()
),
drawer: DrawerList(_getChannels),
),
);
}
}
tabContent.dart
使用listView组件,横线使用SizeBox,图片是使用Row结合网络图片,AspectRadio组件可以让子组件等比缩放,第三个Row左文字右图片方式布局,左文字使用灵活布局Expanded布局,图片使用SizeBox限制固定大小.
class TabBarContent extends StatefulWidget {
final int id;
TabBarContent(this.id);
@override
_TabBarContentState createState() => _TabBarContentState();
}
class _TabBarContentState extends State<TabBarContent> {
List<Article> _list = [];
int page = 1;
ScrollController _controller = ScrollController();
_getData([type]) async{
var data = await PubMoudle.httpRequest('post', '/getarticles', {'id': widget.id, 'page': page});
print(data.data['data']['results']);
List jsonlist = data.data['data']['results'];
List<Article> listData = jsonlist.map((value) => Article.fromJson(value)).toList();
if(type == 1){
setState(() {
_list.addAll(listData);
});
}else{
setState(() {
_list = listData;
});
}
}
Future _refresh() async{
//走接口
_getData();
// setState(() {
// });
}
@override
void initState() {
super.initState();
_getData();
_controller.addListener((){
var maxScroll = _controller.position.maxScrollExtent;
var pixels = _controller.position.pixels;
if(maxScroll == pixels){
//s刷新了
_getData(1);
}
});
}
@override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: _refresh,
child: Padding(
padding: EdgeInsets.all(15.0),
child: ListView.builder(
itemCount: _list.length,
itemBuilder: (context, index){
return GestureDetector(
onTap: (){
Navigator.push(context, MaterialPageRoute(
builder: (context) => DetailPage(_list[index].artId)
));
},
child: NewsItem(_list[index]),
);
},
controller: _controller,
),
),
);
}
}
class NewsItem extends StatelessWidget {
final Article article;
NewsItem(this.article);
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
article.imgType == 1?Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Text(
article.title,
style: TextStyle(
color: Colors.black,
fontSize: 18.0
),
),
),
//图片
SizedBox(
width: 100.0,
height: 100.0,
child: Image.network(
article.images[0],
fit: BoxFit.cover,
),
)
],
): Text(
article.title,
style: TextStyle(
color: Colors.black,
fontSize: 18.0
),
),
SizedBox(height: 6.0,),
article.imgType == 3 ? Row(
children: article.images.map((value){
return Expanded(
child: AspectRatio(
aspectRatio: 4/3,
child: Image.network(
value,
fit: BoxFit.cover,
),
),
);
}).toList()
): SizedBox(height: 6.0,),
RichText(
text: TextSpan(
text: '${article.isTop==1?"置顶 ":""}',
style: TextStyle(
color: Colors.red,
),
children: [
TextSpan(
text: '${article.autName} ',
style: TextStyle(
color: Colors.grey,
),
),
TextSpan(
text: '${article.commCount}评论 ',
style: TextStyle(
color: Colors.grey,
),
),
TextSpan(
text: timeago.format(DateTime.parse(article.pubdate)),
style: TextStyle(
color: Colors.grey,
),
)
]
),
),
Divider(height: 30.0,),
],
);
}
}
在news.dart里写一个获取数据的方_getChannels法,在初始化声明周期里调用这个方法,将数据放在TabBarBtn和TabBarContent里,因为第一次调用是空数据,所以做了三元判断加载DefaultTabController,在map数组循环里用toList()转换
根据上面tab个数渲染TabBarContent,将id传递过来,因为涉及到数据所以需要改用StatefulWidget,在生命周期里调用新闻数据_getData方法,同一页面获取别的组件id参数获取方法用widget.id,在响应体里判断是否字符串和数字的方法,使用序列化,创建article.dart文件,里面用类似构造函数的方法,在dart中跟类写法很像,里面用formJson方法用json进行处理,在响应体里调用,用List listData保留起来,最后将数据放到页面中,在数据定义的时候使用list
使用RefreshIndicator组件,把要刷新的组件写在里面,同时给一个方法,这个方法要用Future声明,同时要用异步写法,其实很简单.
定义一个controller方法,用ScrollController定义,在初始化生命周期里添加监听,监听最大上拉值和pixels值,相等代表刷新,根据每次page++在_getData里使用addAll()方法添加数据.