question_list.dart 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_screenutil/flutter_screenutil.dart';
  3. import 'package:flutter_spinkit/flutter_spinkit.dart';
  4. import 'package:liftmanager/internal/bbs/model/expert_model.dart'
  5. as ExportModel;
  6. import 'package:liftmanager/internal/bbs/model/mix_model.dart';
  7. import 'package:liftmanager/internal/bbs/presenter/question_list_presenter.dart';
  8. import 'package:liftmanager/internal/search/presenter/base_list_provider.dart';
  9. import 'package:liftmanager/mvp/base_page_state.dart';
  10. import 'package:liftmanager/net/api_service.dart';
  11. import 'package:liftmanager/res/iconfont.dart';
  12. import 'package:liftmanager/res/resources.dart';
  13. import 'package:liftmanager/utils/theme_utils.dart';
  14. import 'package:liftmanager/utils/toast.dart';
  15. import 'package:liftmanager/widgets/app_search_bar.dart';
  16. import 'package:liftmanager/widgets/bbs_content.dart';
  17. import 'package:liftmanager/widgets/divider.dart';
  18. import 'package:liftmanager/widgets/load_image.dart';
  19. import 'package:liftmanager/widgets/my_refresh_list.dart';
  20. import 'package:liftmanager/widgets/state_layout.dart';
  21. import 'package:provider/provider.dart';
  22. import '../../brand_page.dart';
  23. class QuestionList extends StatefulWidget {
  24. @override
  25. QuestionListState createState() => QuestionListState();
  26. }
  27. class QuestionListState
  28. extends BasePageState<QuestionList, QuestionListPresenterSeconds> {
  29. BaseListProvider<Records> provider = BaseListProvider<Records>();
  30. ScrollController _scrollController = new ScrollController();
  31. int _page = 1;
  32. int selectedBrandId;
  33. bool showBrandSelectionPanel = false;
  34. bool showExpertSelectionPanel = false;
  35. String selectedBrandName;
  36. String selectedExpertName;
  37. String searchWord;
  38. List<dynamic> brandList;
  39. Map<String, List<ExportModel.Records>> groupedExpertList;
  40. @override
  41. void initState() {
  42. provider.setStateTypeNotNotify(StateType.loading);
  43. super.initState();
  44. _onRefresh();
  45. }
  46. Future getBrandList() async {
  47. await NewApiService().getBrandListType(null, onSuccess: (res) {
  48. if (res != null) {
  49. brandList = res;
  50. setState(() {});
  51. }
  52. }, onError: (code, msg) {
  53. toasts(msg);
  54. });
  55. }
  56. Future getExpertList() async {
  57. await NewApiService().getExpertListGrouped(onSuccess: (res) {
  58. if (res.listGroup != null) {
  59. groupedExpertList = res.listGroup;
  60. setState(() {});
  61. }
  62. }, onError: (code, msg) {
  63. toasts(msg);
  64. });
  65. }
  66. @override
  67. void dispose() {
  68. _scrollController.dispose();
  69. super.dispose();
  70. }
  71. @override
  72. Widget build(BuildContext context) {
  73. double width = MediaQuery.of(context).size.width;
  74. double height = MediaQuery.of(context).size.height;
  75. return ChangeNotifierProvider<BaseListProvider<Records>>(
  76. create: (_) => provider,
  77. child: Scaffold(
  78. appBar: SearchAppBar2(
  79. backgroundColor: Colors.white,
  80. onPressed: (text) {
  81. searchWord = text;
  82. _onRefresh();
  83. },
  84. ),
  85. body: Container(
  86. child: Stack(children: <Widget>[
  87. Column(
  88. children: <Widget>[
  89. Container(
  90. // alignment: Alignment.center,
  91. width: width,
  92. height: ScreenUtil().setWidth(50),
  93. decoration: BoxDecoration(
  94. border: Border(
  95. bottom: BorderSide(width: 0.5, color: Colours.line),
  96. ),
  97. ),
  98. padding: EdgeInsets.only(
  99. left: ScreenUtil().setWidth(15),
  100. right: ScreenUtil().setWidth(15)),
  101. // color: Color(0xFFF1F4FC),
  102. child: Row(
  103. children: <Widget>[
  104. Expanded(
  105. child: Container(
  106. alignment: Alignment.center,
  107. child: GestureDetector(
  108. onTap: () {
  109. getBrandList();
  110. setState(() {
  111. showBrandSelectionPanel =
  112. !showBrandSelectionPanel;
  113. showExpertSelectionPanel = false;
  114. });
  115. },
  116. child: Row(
  117. crossAxisAlignment: CrossAxisAlignment.center,
  118. mainAxisAlignment: MainAxisAlignment.center,
  119. children: <Widget>[
  120. Text(
  121. selectedBrandName ?? '品牌',
  122. style: TextStyle(
  123. fontSize: ScreenUtil().setSp(16),
  124. color: showBrandSelectionPanel
  125. ? Colors.blue
  126. : Colors.black,
  127. ),
  128. textAlign: TextAlign.center,
  129. ),
  130. Container(
  131. // padding: EdgeInsets.only(top: 3),
  132. child: Icon(
  133. showBrandSelectionPanel
  134. ? Icons.keyboard_arrow_up
  135. : Icons.keyboard_arrow_down,
  136. size: 26.0,
  137. color: showBrandSelectionPanel
  138. ? Colors.blue
  139. : Colors.black,
  140. ),
  141. ),
  142. SizedBox(width: 10,),
  143. if (showBrandSelectionPanel && selectedBrandName != null)
  144. GestureDetector(
  145. onTap: (){
  146. setState(() {
  147. showBrandSelectionPanel = false;
  148. selectedBrandName = null;
  149. selectedBrandId = null;
  150. provider.setStateTypeNotNotify(StateType.loading);
  151. _onRefresh();
  152. });
  153. },
  154. child: Container(
  155. padding: EdgeInsets.only(bottom: 3),
  156. child: Icon(
  157. Iconfont.shanchu1,
  158. size: 16.0,
  159. color: Colors.blue,
  160. ),
  161. ),
  162. ),
  163. ],
  164. ),
  165. ),
  166. ),
  167. ),
  168. CzDivider(
  169. height: ScreenUtil().setHeight(15),
  170. width: 2,
  171. ),
  172. // SizedBox(width: ScreenUtil().setWidth(30)),
  173. Expanded(
  174. child: GestureDetector(
  175. onTap: () {
  176. getExpertList();
  177. setState(() {
  178. showExpertSelectionPanel =
  179. !showExpertSelectionPanel;
  180. showBrandSelectionPanel = false;
  181. });
  182. },
  183. child: Row(
  184. mainAxisAlignment: MainAxisAlignment.center,
  185. crossAxisAlignment: CrossAxisAlignment.center,
  186. children: <Widget>[
  187. Text(
  188. selectedExpertName ?? '专家',
  189. style: TextStyle(
  190. fontSize: ScreenUtil().setSp(16),
  191. color: showExpertSelectionPanel
  192. ? Colors.blue
  193. : Colors.black,
  194. ),
  195. textAlign: TextAlign.center,
  196. ),
  197. Container(
  198. // padding: EdgeInsets.only(top: 3),
  199. child: Icon(
  200. showExpertSelectionPanel
  201. ? Icons.keyboard_arrow_up
  202. : Icons.keyboard_arrow_down,
  203. size: 26.0,
  204. color: showExpertSelectionPanel
  205. ? Colors.blue
  206. : Colors.black),
  207. ),
  208. SizedBox(width: 10,),
  209. if (showExpertSelectionPanel && selectedExpertName != null)
  210. GestureDetector(
  211. onTap: (){
  212. setState(() {
  213. showExpertSelectionPanel = false;
  214. selectedExpertName = null;
  215. provider.setStateTypeNotNotify(StateType.loading);
  216. _onRefresh();
  217. });
  218. },
  219. child: Container(
  220. padding: EdgeInsets.only(bottom: 3),
  221. child: Icon(
  222. Iconfont.shanchu1,
  223. size: 16.0,
  224. color: Colors.blue,
  225. ),
  226. ),
  227. ),
  228. ],
  229. ),
  230. ),
  231. )
  232. ],
  233. )),
  234. Expanded(
  235. flex: 1,
  236. child: Consumer<BaseListProvider<Records>>(
  237. builder: (_, provider, __) {
  238. return MyListView(
  239. key: Key('question_list'),
  240. itemCount: provider.list.length,
  241. stateType: provider.stateType,
  242. onRefresh: _onRefresh,
  243. pageSize: 10,
  244. loadMore: _loadMore,
  245. hasMore: provider.hasMore,
  246. padding: EdgeInsets.symmetric(horizontal: 10),
  247. itemBuilder: (_, index) {
  248. return QuestionCell(item: provider.list[index]);
  249. },
  250. );
  251. }))
  252. ],
  253. ),
  254. if (showBrandSelectionPanel)
  255. Positioned.fill(
  256. top: 50,
  257. left: 0,
  258. child: Container(
  259. color: Colors.white,
  260. child: brandList != null
  261. ? BrandSelectionPanel(
  262. brandList: brandList,
  263. onTapBrand: (records) {
  264. setState(() {
  265. selectedBrandName = records.name;
  266. selectedBrandId = records.id;
  267. provider.setStateTypeNotNotify(StateType.loading);
  268. _onRefresh();
  269. showBrandSelectionPanel = false;
  270. });
  271. },
  272. )
  273. : loadCircle(),
  274. ),
  275. ),
  276. if (showExpertSelectionPanel)
  277. Positioned.fill(
  278. top: 50,
  279. left: 0,
  280. child: Container(
  281. color: ThemeUtils.getDialogTextFieldColor(context),
  282. child: groupedExpertList != null
  283. ? ExpertSelectionPanel(
  284. listGroup: groupedExpertList,
  285. onTapExpertCell: (model) {
  286. setState(() {
  287. selectedExpertName = model.name;
  288. showExpertSelectionPanel = false;
  289. provider.setStateTypeNotNotify(StateType.loading);
  290. _onRefresh();
  291. });
  292. },
  293. )
  294. : loadCircle(),
  295. ),
  296. ),
  297. ]),
  298. ),
  299. ),
  300. );
  301. }
  302. Widget loadCircle() {
  303. return Container(
  304. padding: EdgeInsets.only(top: 10, bottom: 10),
  305. color: ThemeUtils.getTabsBg(context),
  306. child: Center(
  307. child: SpinKitFadingCircle(
  308. color: Colors.blueAccent,
  309. size: 30.0,
  310. ),
  311. ),
  312. );
  313. }
  314. Future _onRefresh() async {
  315. _page = 1;
  316. await presenter.getQuestionList(
  317. _page, selectedBrandId, selectedExpertName, searchWord);
  318. }
  319. Future _loadMore() async {
  320. _page++;
  321. await presenter.getQuestionList(
  322. _page, selectedBrandId, selectedExpertName, searchWord);
  323. }
  324. @override
  325. QuestionListPresenterSeconds createPresenter() {
  326. return QuestionListPresenterSeconds();
  327. }
  328. }
  329. class ExpertSelectionPanel extends StatelessWidget {
  330. final Map<String, List<ExportModel.Records>> listGroup;
  331. final Function(ExportModel.Records model) onTapExpertCell;
  332. final _positionAnchors = <String, double>{};
  333. final _controller = ScrollController();
  334. ExpertSelectionPanel({Key key, this.listGroup, this.onTapExpertCell})
  335. : super(key: key) {
  336. double offset = 0;
  337. var sectionHeaderHeight = 30.0;
  338. var sectionCellHeight = 80.0;
  339. for (var key in listGroup.keys) {
  340. _positionAnchors[key] = offset;
  341. offset += sectionHeaderHeight + sectionCellHeight * listGroup[key].length;
  342. }
  343. }
  344. @override
  345. Widget build(BuildContext context) {
  346. return Stack(
  347. alignment: Alignment.center,
  348. children: [
  349. ListView.builder(
  350. itemCount: listGroup.keys.length,
  351. controller: _controller,
  352. itemBuilder: (context, index) {
  353. return Column(
  354. children: [
  355. ExpertSection(
  356. title: listGroup.keys.elementAt(index),
  357. expertList: listGroup[listGroup.keys.elementAt(index)],
  358. onTapExpertCell: onTapExpertCell,
  359. ),
  360. ],
  361. );
  362. }),
  363. Positioned(
  364. right: 0,
  365. child: AlphabeticIndexBar(
  366. controller: _controller,
  367. positionAnchors: _positionAnchors,
  368. ),
  369. ),
  370. ],
  371. );
  372. }
  373. }
  374. class ExpertSection extends StatelessWidget {
  375. final String title;
  376. final List<ExportModel.Records> expertList;
  377. final Function(ExportModel.Records model) onTapExpertCell;
  378. const ExpertSection(
  379. {Key key, this.title, this.expertList, this.onTapExpertCell})
  380. : super(key: key);
  381. @override
  382. Widget build(BuildContext context) {
  383. return Column(children: [
  384. Container(
  385. height: 30,
  386. child: CommonSectionHeader(
  387. title: title,
  388. ),
  389. ),
  390. ...expertList
  391. .map((e) => ExpertCell(
  392. model: e,
  393. onTap: onTapExpertCell,
  394. ))
  395. .toList(),
  396. ]);
  397. }
  398. }
  399. class ExpertCell extends StatelessWidget {
  400. final ExportModel.Records model;
  401. final Function(ExportModel.Records model) onTap;
  402. const ExpertCell({Key key, this.model, this.onTap}) : super(key: key);
  403. @override
  404. Widget build(BuildContext context) {
  405. return GestureDetector(
  406. onTap: () {
  407. if (onTap != null) {
  408. onTap(model);
  409. }
  410. },
  411. child: Container(
  412. color: Colors.white,
  413. height: 80,
  414. padding: EdgeInsets.only(right: 30),
  415. child: Row(
  416. crossAxisAlignment: CrossAxisAlignment.center,
  417. children: [
  418. SizedBox(
  419. width: 10,
  420. ),
  421. ClipRRect(
  422. borderRadius: BorderRadius.circular(24),
  423. child: LoadNetworkImage(
  424. model.avatarUrl,
  425. width: 48,
  426. height: 48,
  427. ),
  428. ),
  429. SizedBox(
  430. width: 10,
  431. ),
  432. Expanded(
  433. child: Column(
  434. mainAxisAlignment: MainAxisAlignment.center,
  435. crossAxisAlignment: CrossAxisAlignment.start,
  436. children: [
  437. Text(model.name ?? ''),
  438. SizedBox(
  439. height: 10,
  440. ),
  441. Text(model.proficiencyBrand ?? ''),
  442. ],
  443. ),
  444. ),
  445. ],
  446. ),
  447. ),
  448. );
  449. }
  450. }