category_page.dart 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. import 'dart:convert';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_easyrefresh/ball_pulse_footer.dart';
  4. import 'package:flutter_easyrefresh/ball_pulse_header.dart';
  5. import 'package:flutter_easyrefresh/easy_refresh.dart';
  6. import 'package:flutter_note/routers/application.dart';
  7. import 'package:flutter_note/model/category_model.dart';
  8. import 'package:flutter_note/model/category_goods_model.dart';
  9. import 'package:flutter_note/provide/mall_goods_provide.dart';
  10. import 'package:flutter_note/provide/sub_category_provide.dart';
  11. import 'package:flutter_note/routers/routers.dart';
  12. import 'package:flutter_note/service/service_method.dart';
  13. import 'package:fluttertoast/fluttertoast.dart';
  14. import 'package:provide/provide.dart';
  15. class CategoryPage extends StatefulWidget {
  16. @override
  17. _CategoryPageState createState() => _CategoryPageState();
  18. }
  19. class _CategoryPageState extends State<CategoryPage> {
  20. List<CategoryData> categories = <CategoryData>[];
  21. GlobalKey<EasyRefreshState> _refreshKey = GlobalKey();
  22. GlobalKey<RefreshHeaderState> _headerKey = GlobalKey();
  23. GlobalKey<RefreshFooterState> _footerKey = GlobalKey();
  24. ScrollController _gridController = ScrollController();
  25. ScrollController _topNavController = ScrollController();
  26. int selectPosition = 0;
  27. @override
  28. void initState() {
  29. super.initState();
  30. _requestCategories();
  31. }
  32. @override
  33. void dispose() {
  34. _gridController.dispose();
  35. _topNavController.dispose();
  36. super.dispose();
  37. }
  38. void _requestCategories() {
  39. getCategories().then((response) {
  40. setState(() {
  41. categories
  42. .addAll(CategoryModel.fromMap(json.decode(response.data)).data);
  43. // 默认初始值列表
  44. Provide.value<SubCategoryProvide>(context)
  45. .changeLeftHeadCategories(categories[0].bxMallSubDto);
  46. Provide.value<SubCategoryProvide>(context)
  47. .changeCategory(categories[0].mallCategoryId);
  48. _requestGoodsList();
  49. });
  50. });
  51. }
  52. // 获取右侧标签下的商品列表
  53. void _requestGoodsList() {
  54. getMallGoods(
  55. Provide.value<SubCategoryProvide>(context).categoryId,
  56. Provide.value<SubCategoryProvide>(context).subCategoryId,
  57. Provide.value<MallGoodsProvide>(context).page)
  58. .then((response) {
  59. Map<String, dynamic> jsonFormat = json.decode(response.data);
  60. // 返回有数据才解析
  61. if (jsonFormat['data'] != null) {
  62. CategoryGoodsModel goods = CategoryGoodsModel.fromMap(jsonFormat);
  63. if (Provide.value<MallGoodsProvide>(context).page == 1) {
  64. Provide.value<MallGoodsProvide>(context).changeGoodsList(goods.data);
  65. } else {
  66. Provide.value<MallGoodsProvide>(context)
  67. .loadMoreGoodsList(goods.data);
  68. }
  69. Provide.value<MallGoodsProvide>(context).increasePage();
  70. } else {
  71. // 无数据返回情况
  72. if (Provide.value<MallGoodsProvide>(context).page == 1)
  73. Provide.value<MallGoodsProvide>(context).changeGoodsList([]);
  74. else {
  75. Fluttertoast.showToast(msg: '没有更多啦~');
  76. Provide.value<MallGoodsProvide>(context).loadMoreGoodsList([]);
  77. }
  78. }
  79. });
  80. }
  81. // 左侧大类导航列表 item
  82. InkWell _leftCategoryItem(int index, BuildContext context) {
  83. return InkWell(
  84. onTap: () {
  85. setState(() => selectPosition = index);
  86. Provide.value<SubCategoryProvide>(context)
  87. .changeLeftHeadCategories(categories[index].bxMallSubDto);
  88. Provide.value<SubCategoryProvide>(context)
  89. .changeCategory(categories[index].mallCategoryId);
  90. Provide.value<MallGoodsProvide>(context).initialPage();
  91. _requestGoodsList();
  92. _gridController.animateTo(0,
  93. duration: Duration(milliseconds: 500), curve: Curves.decelerate);
  94. _topNavController.animateTo(0,
  95. duration: Duration(milliseconds: 300), curve: Curves.decelerate);
  96. },
  97. child: Container(
  98. color: index == selectPosition ? Colors.black12 : Colors.white,
  99. height: 60.0,
  100. child: Text(categories[index].mallCategoryName,
  101. style: TextStyle(fontSize: 15.0, color: Colors.black)),
  102. alignment: Alignment.centerLeft,
  103. padding: const EdgeInsets.symmetric(horizontal: 8.0),
  104. ),
  105. );
  106. }
  107. // 右侧头部导航列表 item
  108. Widget _subCategoryNav(int index, BxMallSubDtoListBean subDto) {
  109. return InkWell(
  110. onTap: () {
  111. Provide.value<SubCategoryProvide>(context)
  112. .changeSubCategorySelect(subDto.mallSubId);
  113. Provide.value<SubCategoryProvide>(context)
  114. .changeSubCategoryIndex(index);
  115. Provide.value<MallGoodsProvide>(context).initialPage();
  116. _requestGoodsList();
  117. _gridController.animateTo(0,
  118. duration: Duration(milliseconds: 500), curve: Curves.decelerate);
  119. },
  120. child: Container(
  121. alignment: Alignment.center,
  122. padding: const EdgeInsets.symmetric(horizontal: 8.0),
  123. child: Text(
  124. subDto.mallSubName,
  125. style: TextStyle(
  126. color:
  127. index == Provide.value<SubCategoryProvide>(context).subIndex
  128. ? Colors.pink
  129. : Colors.black),
  130. ),
  131. ));
  132. }
  133. // 右侧商品列表 item
  134. Widget _goodsItem(CategoryGoodsInfo info, BuildContext context) {
  135. return InkWell(
  136. onTap: () => Application.router
  137. .navigateTo(context, Routers.generateDetailsRouterPath(info.goodsId)),
  138. child: Container(
  139. margin: const EdgeInsets.all(2.0),
  140. alignment: Alignment.center,
  141. color: Colors.white,
  142. child: Column(
  143. children: [
  144. Image.network(info.image,
  145. width: MediaQuery.of(context).size.width * 0.4,
  146. height: MediaQuery.of(context).size.width * 0.4),
  147. Text('${info.goodsName}',
  148. style: TextStyle(fontSize: 14.0, color: Colors.black),
  149. overflow: TextOverflow.ellipsis),
  150. Row(
  151. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  152. crossAxisAlignment: CrossAxisAlignment.end,
  153. children: [
  154. Text('¥${info.presentPrice}',
  155. style: TextStyle(fontSize: 14.0)),
  156. Text('¥${info.oriPrice}',
  157. style: TextStyle(
  158. color: Colors.black26,
  159. decoration: TextDecoration.lineThrough,
  160. fontSize: 12.0))
  161. ])
  162. ],
  163. ),
  164. ),
  165. );
  166. }
  167. @override
  168. Widget build(BuildContext context) {
  169. return Scaffold(
  170. appBar: AppBar(title: Text('商品分类'), centerTitle: true),
  171. body: Row(children: [
  172. // 左侧栏
  173. Container(
  174. width: 100.0,
  175. child: ListView.separated(
  176. itemBuilder: (context, index) =>
  177. _leftCategoryItem(index, context),
  178. separatorBuilder: (context, index) =>
  179. Divider(height: 1.0, color: Colors.black12),
  180. itemCount: categories.length),
  181. ),
  182. // 分割线
  183. VerticalDivider(width: 1.0, color: Colors.black12),
  184. // 右侧栏
  185. Expanded(
  186. child: Column(
  187. children: [
  188. // 头部导航
  189. Provide<SubCategoryProvide>(
  190. builder: (_, child, subCategories) => Container(
  191. color: Colors.white,
  192. height: 50.0,
  193. child: ListView.builder(
  194. controller: _topNavController,
  195. scrollDirection: Axis.horizontal,
  196. itemBuilder: (_, index) => _subCategoryNav(
  197. index, subCategories.subCategories[index]),
  198. itemCount: subCategories.subCategories.length,
  199. ),
  200. ),
  201. ),
  202. Divider(height: 1.0, color: Colors.black12),
  203. // 物品展示
  204. Expanded(
  205. child: Provide<MallGoodsProvide>(
  206. builder: (context, widget, goodsProvide) =>
  207. goodsProvide.goodList.isEmpty
  208. // 当前商品分类无商品情况
  209. ? Center(
  210. child: Column(
  211. mainAxisAlignment: MainAxisAlignment.center,
  212. children: [
  213. Image.asset('images/empty.png',
  214. width: 60.0, height: 60.0),
  215. Text('啊哦...目前未找到该分类下的商品'),
  216. ]),
  217. )
  218. // 当前商品列表下有数据情况
  219. : EasyRefresh(
  220. key: _refreshKey,
  221. refreshHeader: BallPulseHeader(
  222. key: _headerKey, color: Colors.pink),
  223. refreshFooter: BallPulseFooter(
  224. key: _footerKey, color: Colors.pink),
  225. loadMore: () {
  226. _requestGoodsList();
  227. },
  228. child: GridView.builder(
  229. controller: _gridController,
  230. itemCount: goodsProvide.goodList.length,
  231. gridDelegate:
  232. SliverGridDelegateWithFixedCrossAxisCount(
  233. crossAxisCount: 2,
  234. childAspectRatio: 2 / 3,
  235. mainAxisSpacing: 1.0,
  236. crossAxisSpacing: 1.0),
  237. itemBuilder: (_, index) => _goodsItem(
  238. goodsProvide.goodList[index], context)),
  239. ),
  240. ),
  241. )
  242. ],
  243. ))
  244. ]),
  245. );
  246. }
  247. }