search_page.dart 14 KB

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