这篇主要写的是掘金app的沸点页面,效果如下:
先加个Tabbar
之前的文章写过Tabbar的写法。所以直接用就可以。传送门
沸点页面头部内容构建
这部分内容跟首页的类似,可以封装成一个组件,传递数据进去,再应用就可以了。目前是这个效果:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class SearchTop extends StatefulWidget {
SearchTop({Key key}) : super(key: key);
@override
_SearchTopState createState() => _SearchTopState();
}
class _SearchTopState extends State<SearchTop> {
String currentString = '推荐';
tagButton({title = '推荐'}) {
return FlatButton(
onPressed: () {
setState(() {
currentString = title;
});
},
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.symmetric(vertical: 5),
child: Text(title,
style: currentString == title
? TextStyle(fontSize: 20, color: Colors.blue)
: TextStyle(fontSize: 18, color: Colors.grey)),
),
Offstage(
offstage: currentString != title,
child: Container(
height: 2,
width: title.length.toDouble() * 20,
decoration: BoxDecoration(color: Colors.blue),
),
)
],
));
}
renderTag() {
return Container(
width: Get.width,
decoration: BoxDecoration(color: Colors.white),
padding: EdgeInsets.only(
top: Get.context.mediaQueryPadding.top, left: 10, right: 10),
child: Row(
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
'推荐',
'热门',
'关注',
'前端',
'上班摸鱼',
'内推招聘',
'树洞一下',
'今天学到了',
'一图胜千言',
'每日算法题',
].map<Widget>((e) => tagButton(title: e)).toList(),
),
)),
InkWell(
onTap: () {},
child: Container(
padding: EdgeInsets.only(left: 10),
child: Icon(
Icons.list_alt,
color: Colors.grey,
size: 24,
),
),
)
],
),
);
}
@override
Widget build(BuildContext context) {
return renderTag();
}
}
下半部分列表构建
先分析一波页面,分上下两部分,上部分是一个横向滚动的列表,下半部分是沸点内容列表,有图片类型,文字类型,链接类型
横向滚动的列表构建
控制在列表中返回的第一个item为横向滚动视图, 新建search_hot.dart文件
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class SearchHot extends StatefulWidget {
SearchHot({Key key}) : super(key: key);
@override
_SearchHotState createState() => _SearchHotState();
}
class _SearchHotState extends State<SearchHot> {
@override
Widget build(BuildContext context) {
return Container(
height: 100,
width: Get.width - 20,
margin: EdgeInsets.only(top: 10, left: 10),
padding: EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(5)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'🔥有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看',
style: TextStyle(fontSize: 18),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
'点赞89·阅读123·一天清晨',
style: TextStyle(fontSize: 14, color: Colors.grey),
),
)
],
)),
Offstage(
offstage: false,
child: Container(
height: 60,
width: 60,
decoration: BoxDecoration(color: Colors.red),
),
)
],
),
);
}
}
在页面使用
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
SearchTop(),
Expanded(
child: CommonListWiget(
networkApi: (currentPage) async {
Future.delayed(Duration(milliseconds: 500)).then((value) => {
dataList.addAll(['1', '2'])
});
return dataList;
},
itemBuilder: (BuildContext context, int position) {
if (position == 0) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
SearchHot(),
SearchHot(),
SearchHot(),
],
),
);
}
return ListTile(
title: Text('测试' + position.toString()),
subtitle: Text('一些描述'),
);
},
)),
],
));
}
底部列表构建
首先新建search_item.dart文件,先分析主要有,全文字, 文字加图片组合 一步一步来
沸点信息
renderAvatar() {
return Row(
children: [
ClipOval(
child: Container(
height: 40,
width: 40,
decoration: BoxDecoration(color: Colors.red),
),
),
Container(
margin: EdgeInsets.symmetric(horizontal: 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'树洞robot',
style: TextStyle(fontSize: 16, color: Colors.black),
),
Text(
'自动匿名机器人 @ #树洞一下#·31分钟前',
style: TextStyle(fontSize: 14, color: Colors.grey),
),
],
),
)
],
);
}
@override
Widget build(BuildContext context) {
return Container(
width: Get.width,
decoration: BoxDecoration(color: Colors.white),
padding: EdgeInsets.all(15),
margin: EdgeInsets.only(top: 10),
child: Column(
children: [
renderAvatar(),
],
),
);
}
接下来是根据type区分显示文字还是图片
不同状态显示
显示文字,其中包含功能,行数大于3之后,显示展开按钮,点击即展开。
//TQExpandableText 为封装的一个工具widget
renderType() {
if (widget.type == 1) {
//显示文字
return Container(
margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: TQExpandableText(
'有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看',
expandText: '全文',
collapseText: '收起',
maxLines: 3,
style: TextStyle(fontSize: 14, color: Colors.black.withOpacity(0.8)),
),
);
} else if (widget.type == 2) {
//显示文字加图片
} else if (widget.type == 3) {
// 显示文字加图片
}
return Container();
}
显示图片,点击图片有放大效果,主要使用Hero动画完成的
主要代码(ps: 其中图片是在掘金沸点找的一张网络图片😄)
//显示文字加图片
return Hero(
tag: 'imgaesView',
child: Material(
child: InkWell(
onTap: () {
Navigator.push(
context,
new PageRouteBuilder(
transitionDuration: const Duration(milliseconds: 250),
pageBuilder: (context, _, __) => Scaffold(
body: InkWell(
onTap: () {
Navigator.of(context).pop();
},
child: Hero(
tag: 'imgaesView',
child: Container(
width: Get.width,
height: Get.height,
child: Image.network(
'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/39c71164cc4a455b83ac6f579e2a39c1~tplv-k3u1fbpfcp-watermark.image'),
)),
),
),
transitionsBuilder: (_, Animation<double> animation, __,
Widget child) =>
new SlideTransition(
position: new Tween<Offset>(
begin: Offset(0.0, 1.0),
end: Offset(0.0, 0.0),
).animate(animation),
child: child),
));
},
child: Container(
margin: EdgeInsets.only(top: 10, left: 10),
width: 200,
height: 140,
child: Image.network(
'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/39c71164cc4a455b83ac6f579e2a39c1~tplv-k3u1fbpfcp-watermark.image'),
)),
));
最后联动效果
底部列表滑动,标签随之滑动。
修改search_page.dart文件,就ok了。search_hot.dart的滑动和之前写的首页的滑动是一样的,就不把代码贴出来了。一样的方式
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
SearchTop(
dataList: tag,
currentIndex: currentIndex,
onTag: (value) {
setState(() {
currentIndex = value;
});
int index = tag.indexWhere((element) => element == value);
controller.animateToPage(index,
duration: Duration(milliseconds: 500), curve: Curves.easeInOut);
},
),
Expanded(
child: PageView.builder(
controller: controller,
itemBuilder: (context, index) {
return CommonListWiget(
networkApi: (currentPage) async {
Future.delayed(Duration(milliseconds: 500)).then((value) => {
dataList.addAll(['1', '2'])
});
return dataList;
},
itemBuilder: (BuildContext context, int position) {
if (position == 0) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
SearchHot(),
SearchHot(),
SearchHot(),
],
),
);
}
return SearchItem(type: position);
},
);
},
scrollDirection: Axis.horizontal,
onPageChanged: (index) {
print(tag[index]);
setState(() {
currentIndex = tag[index];
});
print(currentIndex);
},
itemCount: tag.length,
)),
],
));
}