search_page.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. import 'dart:convert';
  2. import 'package:flutter/material.dart';
  3. import 'package:liftmanager/internal/bbs/model/banner_model.dart';
  4. import 'package:liftmanager/internal/bbs/model/hot_search_model.dart';
  5. import 'package:liftmanager/internal/search/page/search_index.dart';
  6. import 'package:liftmanager/internal/search/presenter/base_list_provider.dart';
  7. import 'package:liftmanager/net/api_service.dart';
  8. import 'package:liftmanager/res/iconfont.dart';
  9. import 'package:liftmanager/utils/toast.dart';
  10. import 'package:liftmanager/widgets/bbs_content.dart';
  11. import 'package:liftmanager/widgets/load_image.dart';
  12. import 'package:liftmanager/widgets/search_app_bar.dart';
  13. import 'package:shared_preferences/shared_preferences.dart';
  14. class SearchPage extends StatefulWidget {
  15. @override
  16. SearchPageState createState() => SearchPageState();
  17. }
  18. class SearchPageState extends State<SearchPage> {
  19. String searchText;
  20. bool showSearchResult = false;
  21. String noText = "";
  22. List<dynamic> newsList;
  23. List<dynamic> questionList;
  24. List<dynamic> videoList;
  25. List<dynamic> goodsList;
  26. List<dynamic> positionList;
  27. List<dynamic> aggregatedData;
  28. var selectedListType = ListType.all;
  29. var searchHistories = <String>[];
  30. var searchHistoriesUnique = <String>{};
  31. List<HotSearchModel> hotSearches;
  32. BaseListProvider<Records> provider = BaseListProvider<Records>();
  33. @override
  34. initState() {
  35. super.initState();
  36. getSearchHistories();
  37. getHotSearchList();
  38. }
  39. Future getSearchHistories() async {
  40. final prefs = await SharedPreferences.getInstance();
  41. setState(() {
  42. searchHistories.addAll(prefs.getStringList('searchHistory') ?? []);
  43. });
  44. }
  45. Future saveSearchHistories(String newSearchString) async {
  46. searchHistories.insert(0, newSearchString);
  47. if (searchHistories.length > 10) {
  48. searchHistories = searchHistories.sublist(0, 9);
  49. }
  50. for (var h in searchHistories) {
  51. searchHistoriesUnique.add(h);
  52. }
  53. final prefs = await SharedPreferences.getInstance();
  54. return prefs.setStringList('searchHistory', searchHistoriesUnique.toList());
  55. }
  56. Future clearSearchHistories() async {
  57. searchHistories.clear();
  58. final prefs = await SharedPreferences.getInstance();
  59. return prefs.remove('searchHistory');
  60. }
  61. Future getHotSearchList() async {
  62. await NewApiService().getSearchHot(onSuccess: (res) {
  63. setState(() {
  64. hotSearches = res;
  65. });
  66. }, onError: (code, msg) {
  67. toasts(msg);
  68. });
  69. }
  70. Future getSearchList(text) async {
  71. await NewApiService().getSearchIndex(text, onSuccess: (res) {
  72. showSearchResult = true;
  73. questionList = null;
  74. videoList = null;
  75. goodsList = null;
  76. positionList = null;
  77. newsList = null;
  78. if (res.length > 0) {
  79. saveSearchHistories(text);
  80. res.forEach((item) {
  81. if (item.code == "1") {
  82. questionList = item.infoList.take(3).toList();
  83. } else if (item.code == "2") {
  84. videoList = item.infoList.take(3).toList();
  85. } else if (item.code == "3") {
  86. goodsList = item.infoList.take(3).toList();
  87. } else if (item.code == "4") {
  88. positionList = item.infoList.take(3).toList();
  89. } else if (item.code == "5") {
  90. newsList = item.infoList.take(3).toList();
  91. }
  92. });
  93. print(JsonEncoder().convert(res));
  94. print(JsonEncoder().convert(questionList));
  95. print("questionList");
  96. print(JsonEncoder().convert(videoList));
  97. print("videoList");
  98. print(JsonEncoder().convert(goodsList));
  99. print("goodsList");
  100. print(JsonEncoder().convert(positionList));
  101. print("positionList");
  102. setState(() {
  103. aggregatedData = [
  104. [newsList, '新闻', ListType.news],
  105. [videoList, '视频', ListType.video],
  106. [questionList, '问答', ListType.question],
  107. [goodsList, '商品', ListType.goods],
  108. [positionList, '职位', ListType.position]
  109. ];
  110. });
  111. } else {
  112. setState(() {
  113. noText = "暂无数据";
  114. });
  115. }
  116. }, onError: (code, msg) {
  117. toasts(msg);
  118. });
  119. }
  120. @override
  121. Widget build(BuildContext context) {
  122. return Scaffold(
  123. appBar: SearchAppBar(
  124. hintText: "输入搜索内容",
  125. searchText: searchText,
  126. onPressed: (text) {
  127. if (text.isEmpty) {
  128. toasts("搜索关键字不能为空!");
  129. return;
  130. }
  131. setState(() {
  132. searchText = text;
  133. });
  134. aggregatedData = null;
  135. getSearchList(text);
  136. FocusScope.of(context).unfocus();
  137. },
  138. ),
  139. body: LayoutBuilder(
  140. builder: (context, constraints) => Column(
  141. crossAxisAlignment: CrossAxisAlignment.start,
  142. mainAxisAlignment: MainAxisAlignment.center,
  143. children: [
  144. if (showSearchResult && aggregatedData != null) ...[
  145. FilterHeader(
  146. activeIndex: selectedListType.index,
  147. filterNameList:
  148. aggregatedData.map<String>((e) => e[1] as String).toList(),
  149. onTap: (index) {
  150. setState(() {
  151. selectedListType = ListType.values[index];
  152. });
  153. },
  154. ),
  155. Container(
  156. color: Color(0xffF9F9F9),
  157. height: 5,
  158. ),
  159. Container(
  160. height: constraints.maxHeight - 50,
  161. child: selectedListType == ListType.all
  162. ? ListView(
  163. children: [
  164. if (selectedListType == ListType.all) ...listAll()
  165. ],
  166. )
  167. : SearchIndex(selectedListType, searchText),
  168. ),
  169. ],
  170. if (aggregatedData == null &&
  171. searchHistories != null &&
  172. !showSearchResult) ...[
  173. SizedBox(
  174. height: 10,
  175. ),
  176. Row(
  177. children: [
  178. CommonSectionHeader(title: '搜索历史'),
  179. Spacer(),
  180. GestureDetector(
  181. child: Icon(
  182. Iconfont.shanchu1,
  183. color: Color(0xff6A6A76),
  184. size: 16,
  185. ),
  186. onTap: () {
  187. setState(() {
  188. clearSearchHistories();
  189. });
  190. },
  191. ),
  192. SizedBox(
  193. width: 10,
  194. ),
  195. ],
  196. ),
  197. SizedBox(
  198. height: 10,
  199. ),
  200. Expanded(
  201. child: historicalSearchSection(),
  202. ),
  203. SizedBox(
  204. height: 10,
  205. ),
  206. CommonSectionHeader(title: '热门搜索'),
  207. if (hotSearches != null)
  208. Expanded(
  209. flex: 3,
  210. child: hotSearchSection(),
  211. ),
  212. SizedBox(
  213. height: 10,
  214. ),
  215. ],
  216. if (showSearchResult && aggregatedData == null)
  217. Center(child: Text(noText))
  218. ],
  219. ),
  220. ),
  221. );
  222. }
  223. List<Widget> listAll() {
  224. return [
  225. for (var e in aggregatedData)
  226. if (e[0] != null && (e[0] as List).length > 0)
  227. Column(children: [
  228. SectionHeader(
  229. title: e[1],
  230. ),
  231. if (e[2] == ListType.news)
  232. Container(
  233. padding: EdgeInsets.symmetric(horizontal: 10),
  234. child: HotNews(newsList: e[0]),
  235. ),
  236. if (e[2] == ListType.video)
  237. Container(
  238. padding: EdgeInsets.symmetric(horizontal: 10),
  239. child: Column(
  240. children: HotClass(videoList: e[0]).listVideo(context),
  241. ),
  242. ),
  243. if (e[2] == ListType.question)
  244. Container(
  245. padding: EdgeInsets.symmetric(horizontal: 10),
  246. child: HotQuestion(initList: e[0]),
  247. ),
  248. if (e[2] == ListType.goods)
  249. Container(
  250. padding: EdgeInsets.symmetric(horizontal: 10),
  251. child: HotProduct(productList: e[0]),
  252. ),
  253. if (e[2] == ListType.position)
  254. Container(
  255. padding: EdgeInsets.symmetric(horizontal: 10),
  256. child: HotPosition(positionList: e[0]),
  257. ),
  258. GestureDetector(
  259. onTap: () {
  260. setState(() {
  261. selectedListType = e[2];
  262. });
  263. },
  264. child: Container(
  265. height: 40,
  266. padding: EdgeInsets.symmetric(horizontal: 10),
  267. child: Row(children: [
  268. Icon(
  269. Iconfont.sousuo,
  270. color: Color(0xff6A6A76),
  271. size: 16,
  272. ),
  273. SizedBox(
  274. width: 10,
  275. ),
  276. Expanded(
  277. child: Text(
  278. '更多相关${e[1]}',
  279. style: TextStyle(
  280. color: Color(0xff9A9A9A),
  281. fontSize: 14,
  282. ),
  283. ),
  284. ),
  285. Icon(
  286. Iconfont.gengduo,
  287. size: 11,
  288. color: Colors.grey,
  289. ),
  290. ]),
  291. ),
  292. ),
  293. Container(
  294. color: Color(0xffF9F9F9),
  295. height: 5,
  296. ),
  297. ])
  298. ];
  299. }
  300. Widget historicalSearchSection() {
  301. List<LayoutId> widgetList = [];
  302. for (var i = 0; i < searchHistories.length; i++) {
  303. widgetList.add(
  304. LayoutId(
  305. id: i,
  306. child: GestureDetector(
  307. onTap: () {
  308. setState(() {
  309. searchText = searchHistories[i];
  310. });
  311. getSearchList(searchHistories[i]);
  312. },
  313. child: Container(
  314. padding: EdgeInsets.all(10),
  315. decoration: BoxDecoration(
  316. color: Color(0xffF4F8FA),
  317. borderRadius: BorderRadius.all(Radius.circular(16)),
  318. ),
  319. child: Text(
  320. searchHistories[i],
  321. style: TextStyle(color: Color(0xff666666), fontSize: 13),
  322. ),
  323. ),
  324. ),
  325. ),
  326. );
  327. }
  328. return CustomMultiChildLayout(
  329. children: widgetList,
  330. delegate: SearchHistorySectionLayoutDelegate(
  331. widgetList.map<int>((e) => e.id).toList()),
  332. );
  333. }
  334. Widget hotSearchSection() {
  335. return Container(
  336. margin: EdgeInsets.all(10),
  337. padding: EdgeInsets.all(10),
  338. decoration: BoxDecoration(
  339. color: Colors.white,
  340. borderRadius: BorderRadius.all(
  341. Radius.circular(5),
  342. ),
  343. boxShadow: [
  344. BoxShadow(
  345. color: Colors.grey.withOpacity(0.2),
  346. spreadRadius: 1,
  347. blurRadius: 3,
  348. offset: Offset(0, 0), // changes position of shadow
  349. ),
  350. ]),
  351. child: ListView.builder(
  352. itemCount: hotSearches.length > 10 ? 10 : hotSearches.length,
  353. itemBuilder: (context, index) {
  354. return SizedBox(
  355. height: 35,
  356. child: GestureDetector(
  357. onTap: (){
  358. setState(() {
  359. searchText = hotSearches[index].keyword;
  360. });
  361. getSearchList(hotSearches[index].keyword);
  362. }, child: Row(
  363. children: [
  364. if (index == 0) LoadAssetImage('icon_crown_golden'),
  365. if (index == 1) LoadAssetImage('icon_crown_silver'),
  366. if (index == 2) LoadAssetImage('icon_crown_bronze'),
  367. if (index > 2) LoadAssetImage('icon_number_${index + 1}'),
  368. SizedBox(width: 10),
  369. Text(
  370. hotSearches[index].keyword,
  371. style: TextStyle(
  372. fontSize: 13,
  373. color: Color(0xff666666),
  374. ),
  375. ),
  376. Spacer(),
  377. if (index == 0)
  378. Icon(
  379. Iconfont.remen,
  380. size: 14,
  381. color: Color(0xffF95046),
  382. ),
  383. if (index == 1)
  384. Icon(
  385. Iconfont.remen,
  386. size: 14,
  387. color: Color(0xffFDAF2C),
  388. ),
  389. if (index == 2)
  390. Icon(
  391. Iconfont.remen,
  392. size: 14,
  393. color: Color(0xffFEE455),
  394. ),
  395. SizedBox(width: 10),
  396. ],
  397. ),
  398. ),);
  399. }),
  400. );
  401. }
  402. }
  403. class SearchHistorySectionLayoutDelegate extends MultiChildLayoutDelegate {
  404. List<int> childIds;
  405. SearchHistorySectionLayoutDelegate(this.childIds);
  406. @override
  407. void performLayout(Size size) {
  408. Size leaderSize = Size.zero;
  409. const spacing = 10.0;
  410. const rightMargin = 10.0;
  411. var offset = Offset.zero;
  412. for (var id in childIds) {
  413. if (hasChild(id)) {
  414. offset = Offset(offset.dx + leaderSize.width + spacing, offset.dy);
  415. leaderSize = layoutChild(
  416. id,
  417. BoxConstraints.loose(size),
  418. );
  419. if (size.width - offset.dx - leaderSize.width <= rightMargin) {
  420. offset = Offset(spacing, offset.dy + leaderSize.height + spacing);
  421. }
  422. positionChild(id, offset);
  423. }
  424. }
  425. }
  426. @override
  427. bool shouldRelayout(covariant MultiChildLayoutDelegate oldDelegate) {
  428. return false;
  429. }
  430. }