接着上文,把掘金app的首页完善一下。不同标签下的展示是不同的样式。
根据标签改变内容
效果:
思路是把点击标签的事件传递出来,然后判断标签展示不用的内容
点击事件传递home_top.dart文件中,tagButton方法里面传递参数,这里只截取一部分代码
tagButton({title = '标签'}) {
return FlatButton(
onPressed: () {
setState(() {
currentString = title;
});
widget.onPress(title);
},
)}
在home_list.dart中接受参数,显示不同内容
@override
Widget build(BuildContext context) {
return CommonListWiget(
scrollController: _scrollController,
networkApi: (currentPage) async {
Future.delayed(Duration(seconds: 2)).then((value) => {
dataList.addAll(['1', '2'])
});
return dataList;
},
itemBuilder: (BuildContext context, int position) {
if (position == 0) {
return widget.tag == '关注' ? peopleItems() : hotWidget();
}
return listItemWidget();
},
);
}
其中的peopleItems方法内容就是页面构造的内容:
// Global.randomColor() 写的一个随机颜色的取值方法,写在了Utils文件夹的Global文件里面
renderAvatar(index) {
return Positioned(
right: 18 + index * 16.toDouble(),
top: 15,
child: ClipOval(
child: Container(
height: 20,
width: 20,
decoration: BoxDecoration(color: Global.randomColor()),
),
));
}
peopleItems() {
return Container(
height: 50,
padding: EdgeInsets.symmetric(horizontal: 10),
margin: EdgeInsets.only(top: 10),
decoration: BoxDecoration(color: Colors.white),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'发现更多掘金优秀作者',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
Expanded(
child: Stack(
children: [
renderAvatar(0),
renderAvatar(1),
renderAvatar(2),
renderAvatar(3),
Positioned(
right: 0,
top: 15,
child: Icon(
Icons.keyboard_arrow_right,
color: Colors.grey,
size: 18,
),
)
],
)),
],
),
);
}
展示不同标签下的功能标签
效果:
思路是在home_top.dart中写逻辑代码
String currentTag = '全部';
renderTagType() {
return Offstage(
offstage: currentString == '关注' || currentString == '推荐',
child: Container(
height: 50,
width: Get.width,
padding: EdgeInsets.symmetric(horizontal: 10),
margin: EdgeInsets.only(top: 95 + (0 - 40 * widget.alpha / 255)),
child: Row(
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
renderTagTypeButton('全部'),
renderTagTypeButton('Java'),
renderTagTypeButton('后端'),
renderTagTypeButton('Spring Boot'),
renderTagTypeButton('Go'),
renderTagTypeButton('Python'),
renderTagTypeButton('MySQL'),
renderTagTypeButton('Spring'),
renderTagTypeButton('Redius'),
renderTagTypeButton('JVM'),
renderTagTypeButton('算法'),
renderTagTypeButton('数据库'),
],
),
)),
InkWell(
onTap: () {},
child: Icon(
Icons.keyboard_arrow_right,
color: Colors.grey,
size: 18,
),
),
],
),
),
);
}
renderTagTypeButton(title) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
child: FlatButton(
height: 30,
minWidth: 20,
shape: StadiumBorder(),
padding: EdgeInsets.symmetric(horizontal: 10),
color: currentTag == title
? Colors.blue
: Colors.grey.withOpacity(0.2),
onPressed: () {
setState(() {
currentTag = title;
});
},
child: Text(
title,
style: TextStyle(
fontSize: 14,
color: currentTag == title ? Colors.white : Colors.grey),
)));
}
最终把方法加入到build中
//renderTag 是标签方法,被提取出来放到一个方法里面
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: Get.context.mediaQueryPadding.top),
decoration: BoxDecoration(color: Colors.white),
child: Stack(
children: [renderSearch(), renderTag(), renderTagType()],
),
);
}
实现最终效果
分两步来实现,第一步点击展开的时候,将标签展开
第一步
其中WidgetUtil方法用到之前集成的库flustars获取的每个标签的宽度,存储到scrollerIndex数组里面。这样每次点击可以进行滑动到指定位置操作。
//判断是否展开
bool isExp = false;
ScrollController tagScroller = new ScrollController();
// 记录滚动位置数组
List scrollerIndex = [];
renderTagTypeButton(title) {
return Builder(builder: (BuildContext context) {
Rect rect = WidgetUtil.getWidgetBounds(context);
double width = rect.right +
(scrollerIndex.length > 0
? scrollerIndex[scrollerIndex.length - 1]
: 0);
if (scrollerIndex.length < data.length) {
scrollerIndex.add(width);
}
return Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
child: FlatButton(
height: 30,
minWidth: 20,
shape: StadiumBorder(),
padding: EdgeInsets.symmetric(horizontal: 10),
color: currentTag == title
? Colors.blue
: Colors.grey.withOpacity(0.2),
onPressed: () {
setState(() {
currentTag = title;
isExp = false;
});
int index = data.indexWhere((element) => element == title);
Future.delayed(Duration(milliseconds: 200)).then((value) => {
tagScroller.jumpTo(
scrollerIndex.length > 0 && index - 3 > 0
? scrollerIndex[index - 3]
: 0)
});
},
child: Text(
title,
style: TextStyle(
fontSize: 14,
color: currentTag == title ? Colors.white : Colors.grey),
)));
});
}
然后在添加下拉的点击方法中isExp字段判断,如果展开是用Wrap去构建。
renderTagType() {
return Offstage(
offstage: currentString == '关注' || currentString == '推荐',
child: Container(
width: Get.width,
padding: EdgeInsets.symmetric(horizontal: 10),
margin: EdgeInsets.only(top: 95 + (0 - 40 * widget.alpha / 255)),
child: Row(
children: [
Expanded(
child: isExp
? Wrap(
children: data
.map<Widget>((e) => renderTagTypeButton(e))
.toList(),
)
: SingleChildScrollView(
scrollDirection: Axis.horizontal,
controller: tagScroller,
child: Row(
children: data
.map<Widget>((e) => renderTagTypeButton(e))
.toList(),
),
)),
Offstage(
offstage: isExp,
child: InkWell(
onTap: () {
setState(() {
isExp = !isExp;
});
},
child: Container(
padding: EdgeInsets.only(left: 10),
child: Icon(
Icons.keyboard_arrow_down,
color: Colors.grey,
size: 24,
),
),
))
],
),
),
);
}
第二步
效果:
以上代码还没有完全实现其功能,在展开的时候列表还是可以滑动的,这个时候我们去给列表添加一个遮盖,使其不能操作即可。
其实很好实现,在home_list.dart文件中,增加stack组件,判断是否是展开状态,如果是则添加遮盖,隔断响应手势的方法。就可以了。
首先我们需要把是否展开的状态传递出去,在home_top.dart中定义
final Function onTypePress;
点击对应部分传参数出去
//true展开,false未展开
widget.onTypePress(false);
对应位置就是点击标签之后
renderTagTypeButton(
...省略代码
);
tagButton(
...省略代码
)
两个方法中使用 接着在home_list.dart文件中添加遮盖。
final bool isExp;
@override
Widget build(BuildContext context) {
return Stack(
children: [
CommonListWiget(
scrollController: _scrollController,
networkApi: (currentPage) async {
Future.delayed(Duration(seconds: 2)).then((value) => {
dataList.addAll(['1', '2'])
});
return dataList;
},
itemBuilder: (BuildContext context, int position) {
if (position == 0 && widget.tag == '关注') {
return peopleItems();
} else if (position == 0 && widget.tag == '推荐') {
return hotWidget();
}
return listItemWidget();
},
),
Positioned(
top: widget.isExp ? 0 : Get.height,
child: InkWell(
onTap: () {},
child: Container(
height: Get.height,
width: Get.width,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.1),
),
),
)),
],
);
}
最后在home_page.dart文件中去传递参数
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(235, 242, 244, 1),
body: Column(
children: [
HomeTopPage(
onSearchPress: () {},
onTagPress: () {},
alpha: _alpha,
onTypePress: (isBool) {
setState(() {
isExp = isBool;
});
},
onPress: (title) {
setState(() {
tag = title;
});
}),
Expanded(
child: HomeList(
onScroll: (value) {
setState(() {
_alpha = value;
});
},
tag: tag,
isExp: isExp)),
],
));
}
搜索页面
这个页面需要注意的问题,没有太多,都是一些基本的逻辑和编码,所以直接看效果上代码了,两个页面的代码都在下面了
import 'package:flutter/material.dart';
import 'package:flutter_common_app/widget/common_list.dart';
import 'package:get/get.dart';
import 'package:sp_util/sp_util.dart';
class HomeSearchPage extends StatefulWidget {
HomeSearchPage({Key key}) : super(key: key);
@override
_HomeSearchPageState createState() => _HomeSearchPageState();
}
class _HomeSearchPageState extends State<HomeSearchPage> {
String currentString = '综合';
PageController controller = new PageController();
List dataList = [];
List historyList = [];
@override
void initState() {
super.initState();
List temp = SpUtil.getStringList('historyKey').toList();
setState(() {
historyList = temp;
});
}
renderHistory() {
return Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 20),
height: 40,
decoration: BoxDecoration(color: Colors.white),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'搜索历史',
style: TextStyle(
fontSize: 18, color: Colors.grey.withOpacity(0.8)),
),
IconButton(
icon: Icon(
Icons.auto_delete,
size: 18,
color: Colors.grey,
),
onPressed: () {
SpUtil.putStringList('historyKey', []);
setState(() {
historyList = [];
});
})
],
),
Wrap(
children: historyList
.map<Widget>((e) => FlatButton(
height: 30,
minWidth: 20,
shape: StadiumBorder(),
padding: EdgeInsets.symmetric(horizontal: 10),
color: Colors.grey.withOpacity(0.2),
onPressed: () {},
child: Text(
e,
style: TextStyle(fontSize: 14, color: Colors.grey),
)))
.toList(),
)
],
),
);
}
tagButton({title = '综合'}) {
return FlatButton(
onPressed: () {
setState(() {
currentString = title;
});
int tag = 0;
if (title == '综合') {
tag = 0;
} else if (title == '文章') {
tag = 1;
} else if (title == '标签') {
tag = 2;
} else if (title == '用户') {
tag = 3;
}
controller.animateToPage(tag,
duration: Duration(milliseconds: 500), curve: Curves.easeInOut);
},
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.symmetric(vertical: 5),
child: Text(title,
style: currentString == title
? TextStyle(fontSize: 18, color: Colors.blue)
: TextStyle(fontSize: 18, color: Colors.grey)),
),
Offstage(
offstage: currentString != title,
child: Container(
height: 2,
width: 40,
decoration: BoxDecoration(color: Colors.blue),
),
)
],
));
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Container(
decoration: BoxDecoration(color: Colors.white),
padding: EdgeInsets.only(top: Get.context.mediaQueryPadding.top),
child: Row(
children: [
Expanded(
child: Container(
margin: EdgeInsets.only(left: 10),
height: 40,
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.2),
borderRadius: BorderRadius.circular(8)),
child: Row(
children: [
Padding(padding: EdgeInsets.symmetric(horizontal: 5)),
Icon(
Icons.search,
size: 18,
color: Colors.grey.withOpacity(0.5),
),
Padding(padding: EdgeInsets.symmetric(horizontal: 5)),
Expanded(
child: TextField(
autofocus: true,
onSubmitted: (value) {
List temp = SpUtil.getStringList('historyKey').toList();
temp.add(value);
SpUtil.putStringList('historyKey', temp);
setState(() {
historyList = temp;
dataList = value.contains('一天清晨') ? ['1', '1'] : [];
});
},
decoration: InputDecoration(
hintText: '搜索文章/标签/用户',
// border: InputBorder.none,
contentPadding: EdgeInsets.only(top: 5),
border: OutlineInputBorder(
borderSide: BorderSide(
width: 0, color: Colors.transparent)),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 0, color: Colors.transparent)),
disabledBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 0, color: Colors.transparent)),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 0, color: Colors.transparent)),
),
style: TextStyle(fontSize: 14, color: Colors.grey),
))
],
),
)),
FlatButton(
minWidth: 20,
onPressed: () {
Get.back();
},
child: Text('取消'),
textColor: Colors.blue),
],
),
),
Container(
decoration: BoxDecoration(color: Colors.white),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
tagButton(title: '综合'),
tagButton(title: '文章'),
tagButton(title: '标签'),
tagButton(title: '用户'),
],
),
),
Expanded(
child: dataList.length == 0
? renderHistory()
: PageView.builder(
controller: controller,
itemBuilder: (context, index) {
return CommonListWiget(
networkApi: (currentPage) async {
// Future.delayed(Duration(seconds: 2)).then((value) => {
// dataList.addAll(['1', '2'])
// });
return dataList;
},
itemBuilder: (BuildContext context, int position) {
return Container(
padding: EdgeInsets.all(10),
margin: EdgeInsets.only(top: 5),
decoration: BoxDecoration(color: Colors.white),
child: Row(
children: [
Container(
height: 50,
width: 50,
margin: EdgeInsets.only(right: 10),
decoration: BoxDecoration(
color: Colors.red,
borderRadius:
BorderRadius.all(Radius.circular(5))),
),
Expanded(
child: Container(
height: 50,
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'前端',
style: TextStyle(
fontSize: 16, color: Colors.grey),
),
Text(
'50.0w 人关注·5.2w文章',
style: TextStyle(
fontSize: 14, color: Colors.grey),
),
],
),
)),
Container(
height: 30,
width: 70,
alignment: Alignment.center,
margin: EdgeInsets.only(left: 10),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.3),
borderRadius: BorderRadius.circular(15),
),
child: Text('已关注',
style: TextStyle(
fontSize: 14, color: Colors.grey)),
)
],
),
);
},
);
},
scrollDirection: Axis.horizontal,
onPageChanged: (index) {
String tag = '';
if (index == 0) {
tag = '综合';
} else if (index == 1) {
tag = '文章';
} else if (index == 2) {
tag = '标签';
} else if (index == 3) {
tag = '用户';
}
setState(() {
currentString = tag;
});
},
itemCount: 4,
))
],
));
}
}
标签页面
同上个页面
import 'package:flutter/material.dart';
import 'package:flutter_common_app/widget/common_app_bar.dart';
import 'package:flutter_common_app/widget/common_list.dart';
class HomeTagPage extends StatefulWidget {
HomeTagPage({Key key}) : super(key: key);
@override
_HomeTagPageState createState() => _HomeTagPageState();
}
class _HomeTagPageState extends State<HomeTagPage> {
String currentString = '全部标签';
PageController controller = new PageController();
List dataList = ['1', '1', '1'];
tagButton({title = '全部标签'}) {
return FlatButton(
onPressed: () {
setState(() {
currentString = title;
});
controller.animateToPage(title == '全部标签' ? 0 : 1,
duration: Duration(milliseconds: 500), curve: Curves.easeInOut);
},
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.symmetric(vertical: 5),
child: Text(title,
style: currentString == title
? TextStyle(fontSize: 18, color: Colors.blue)
: TextStyle(fontSize: 18, color: Colors.grey)),
),
Offstage(
offstage: currentString != title,
child: Container(
height: 2,
width: 80,
decoration: BoxDecoration(color: Colors.blue),
),
)
],
));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: customAppbar(title: '标签管理'),
body: Column(
children: [
Container(
decoration: BoxDecoration(color: Colors.white),
margin: EdgeInsets.only(top: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
tagButton(title: '全部标签'),
tagButton(title: '已关注标签'),
],
),
),
Expanded(
child: PageView.builder(
controller: controller,
itemBuilder: (context, index) {
return CommonListWiget(
networkApi: (currentPage) async {
Future.delayed(Duration(seconds: 2)).then((value) => {
dataList.addAll(['1', '2'])
});
return dataList;
},
itemBuilder: (BuildContext context, int position) {
return Container(
padding: EdgeInsets.all(10),
margin: EdgeInsets.only(top: 5),
decoration: BoxDecoration(color: Colors.white),
child: Row(
children: [
Container(
height: 50,
width: 50,
margin: EdgeInsets.only(right: 10),
decoration: BoxDecoration(
color: Colors.red,
borderRadius:
BorderRadius.all(Radius.circular(5))),
),
Expanded(
child: Container(
height: 50,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'前端',
style:
TextStyle(fontSize: 16, color: Colors.grey),
),
Text(
'50.0w 人关注·5.2w文章',
style:
TextStyle(fontSize: 14, color: Colors.grey),
),
],
),
)),
Container(
height: 30,
width: 70,
alignment: Alignment.center,
margin: EdgeInsets.only(left: 10),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.3),
borderRadius: BorderRadius.circular(15),
),
child: Text('已关注',
style:
TextStyle(fontSize: 14, color: Colors.grey)),
)
],
),
);
},
);
},
scrollDirection: Axis.horizontal,
onPageChanged: (index) {
setState(() {
currentString = index == 0 ? '全部标签' : '已关注标签';
});
},
itemCount: 2,
))
],
),
);
}
}
完成。项目纯属没事写着完呢。 欢迎指导留言谈论
one more things...
-
掘金app沸点