项目git地址:flower_gift
tabBar-分类界面:
效果:
头部搜索栏的搭建:
//顶部搜索框;
Widget _buildSearchBar(){
return Container(
margin: EdgeInsets.symmetric(horizontal: 10.0,vertical: 8.0),
decoration: BoxDecoration(
color: Colors.black12,
borderRadius: BorderRadius.all(Radius.circular(22.0)),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: TextFormField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: "请输入关键字",
hintStyle: TextStyle(
color: Colors.black
),
icon: Icon(Icons.search,color:Colors.black)
),
),
)
),
],
),
);
}
使用:
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: _buildSearchBar()
),
body: ...
这里就是说,开始我以为这个Scaffold的title属性只能是这个Text组件控制这个导航栏title.原来这个也是完全可以自定义的。
body布局部分:
body: FutureBuilder(
future: _getCategoryPage(context),
builder: (context,snapshot){
if(snapshot.hasData){
return Container(
color: Colors.white,
child: Row(
children: [
Expanded(
flex: 2,
child: CategoryLeftNav()
),
Expanded(
flex: 5,
child: CategoryRightCategory()
)
],
),
);
}else{
return Center(
child: Text("暂无数据"),
);
}
},
),
//网络请求
Future _getCategoryPage(BuildContext context) async{
await Provide.value(context).getCategoryPageData();
return "完成加载....";
}
整体布局我把这个作为左右两部分吧,所以这个就直接用Row,然后里面嵌套这个Expanded组件,用flex来控制所占宽度比。
左边导航栏
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../model/categoryPage_model_entity.dart';
import 'package:provide/provide.dart';
import '../../provide/categoryPage_provide.dart';
class CategoryLeftNav extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Provide(
builder: (context,child,val){
var categoryPageModelEntity = val.categoryModelEntity;
return Container(
decoration: BoxDecoration(
border: Border(
right: BorderSide(
color: Colors.black12,
width: 1.0,
style: BorderStyle.solid
)
)
),
child: ListView.builder(
itemCount: categoryPageModelEntity.categorys.length,
itemBuilder:(context,index){
return Container(
color: Colors.white,
child: InkWell(
//设置点击闪烁那一下的文字颜色
splashColor: Colors.redAccent.withOpacity(0.5),
onTap: (){
Provide.value(context).switchCategory(index);
},
child: Column(
children: [
SizedBox(height: 15.0,),
Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(vertical: 4.0),
decoration: (Provide.value(context).currentIndex == index) ? BoxDecoration(
border: Border(
left: BorderSide(
color: Colors.orange,
width: 3.0,
style: BorderStyle.solid
)
)
) : null,
child: Text(
categoryPageModelEntity.categorys[index].type,
style: TextStyle(fontSize: 18.0),
),
),
SizedBox(height: 15.0,),
],
)
)
);
},
),
);
}
);
}
}
这个布局就比较简单的,主要就是利用Provide来管理了当前点击的分类项,然后左边的指示条我就直接用的这个边框来做的。
右边分类布局部分:
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../model/categoryPage_model_entity.dart';
import 'package:provide/provide.dart';
import '../../provide/categoryPage_provide.dart';
class CategoryRightCategory extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Provide(
builder: (context,child,val){
var currentIndex = Provide.value(context).currentIndex;
var categoryModelCategory = val.categoryModelEntity.categorys[currentIndex];
var categoryBanners = categoryModelCategory.categoryBanner;
var areasItem = categoryModelCategory.areas[0];//默认只做第一排的商品分类详情了
var head = areasItem.head;//看看是否有头部标题栏
var contents = areasItem.contents;
return Container(
child:ListView(
children: [
_buildTopBanner(context, categoryModelCategory.banner),
(categoryBanners != null) ? _buildCategoryBanner(context, categoryBanners) : SizedBox(height: 1.0,),
(head != null) ? _buildTitle(head) : SizedBox(height: 1.0,),
Divider(),
_buildCategoryShow(context, contents),
],
)
);
},
);
}
//顶部banner
Widget _buildTopBanner(BuildContext context,CategoryModelCategorysBanner banner){
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
child: Container(
height: MediaQuery.of(context).size.height / 7,
child: Image.network(banner.imageUrl,fit:BoxFit.fill,),
),
);
}
//分类banner -- 需要判断显示否
Widget _buildCategoryBanner(BuildContext context,List categoryBanners){
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Container(
height: MediaQuery.of(context).size.height / 7,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: _categoryBanners(context, categoryBanners)
),
),
);
}
List _categoryBanners(BuildContext context, List categoryBanners){
List list = new List();
for(var i = 0; i < categoryBanners.length; i++){
list.add(
Container(
width: (MediaQuery.of(context).size.width / 7 * 5 - 40.0)/3,
child: Image.network(categoryBanners[i].imageUrl,fit:BoxFit.fill),
)
);
}
return list;
}
//标题栏
Widget _buildTitle(dynamic head){
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0,vertical: 5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(head["Title"]),
Text(head["UrlDesc"])
],
),
);
}
//分类展示
Widget _buildCategoryShow(BuildContext context,List contents){
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Wrap(
alignment: WrapAlignment.spaceBetween,
children: _categoryShow(context, contents)
),
);
}
//分类展示遍历
List _categoryShow(BuildContext context,List contents){
List list = new List();
for(var i = 0; i < contents.length; i++){
list.add(
Container(
// height: MediaQuery.of(context).size.height / 7 + 20.0,
width: (MediaQuery.of(context).size.width / 7 * 5 - 30.0)/3,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height:25.0),
(contents[i].text != null) ? CircleAvatar(
child: Image.network(contents[i].imageUrl),
) : Padding(
padding: const EdgeInsets.symmetric(horizontal: 5.0),
child:Image.network(contents[i].imageUrl),
),
SizedBox(height: 15.0,),
(contents[i].text != null) ? Text(
contents[i].text
) : SizedBox(height: 1.0,),
],
),
)
);
}
return list;
}
}
这边布局也是比较简单的,主要就是考虑这个数据是否为null,来确定显示哪一部分。