question_list.dart 18 KB

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