基础组件库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

feedback_page.dart 16 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. import 'dart:io';
  2. import 'package:flutter/cupertino.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:fluttertoast/fluttertoast.dart';
  5. import 'package:image_picker/image_picker.dart';
  6. import 'package:permission_handler/permission_handler.dart';
  7. import 'package:zhiying_base_widget/pages/feedback_page/bloc/feedback_bloc.dart';
  8. import 'package:zhiying_base_widget/pages/feedback_page/bloc/feedback_repository.dart';
  9. import 'package:zhiying_base_widget/pages/feedback_page/feedback_record_page.dart';
  10. import 'package:zhiying_base_widget/pages/feedback_page/model/feedback_model.dart';
  11. import 'package:zhiying_comm/zhiying_comm.dart';
  12. import 'package:zhiying_base_widget/pages/feedback_page/widgets/feedback_image.dart';
  13. import 'package:zhiying_base_widget/pages/feedback_page/widgets/feedback_tab_widget.dart';
  14. import 'package:zhiying_base_widget/widgets/others/action_selected_alert/action_selected_alert.dart';
  15. import 'package:zhiying_comm/util/log/let_log.dart';
  16. import 'package:flutter_bloc/flutter_bloc.dart';
  17. import 'bloc/feedback_bloc.dart';
  18. import 'bloc/feedback_repository.dart';
  19. import 'bloc/feedback_state.dart';
  20. import 'bloc/feedback_event.dart';
  21. import 'package:fluttertoast/fluttertoast.dart';
  22. ///
  23. /// 意见反馈
  24. ///
  25. class FeedbackPage extends StatefulWidget {
  26. final Map<String, dynamic> data;
  27. FeedbackPage(this.data);
  28. @override
  29. _FeedbackPageState createState() => _FeedbackPageState();
  30. }
  31. class _FeedbackPageState extends State<FeedbackPage> {
  32. @override
  33. Widget build(BuildContext context) {
  34. return BlocProvider<FeedbackBloc>(
  35. create: (_) => FeedbackBloc(FeedBackRepository())..add(FeedbackInitEvent()),
  36. child: _FeedbackPageContainer(),
  37. );
  38. }
  39. }
  40. class _FeedbackPageContainer extends StatefulWidget {
  41. @override
  42. __FeedbackPageContainerState createState() => __FeedbackPageContainerState();
  43. }
  44. class __FeedbackPageContainerState extends State<_FeedbackPageContainer> {
  45. List<File> _images = List();
  46. bool _submitable = false;
  47. TextEditingController _feedback;
  48. TextEditingController _title;
  49. @override
  50. void initState() {
  51. _feedback = TextEditingController();
  52. _title = TextEditingController();
  53. super.initState();
  54. }
  55. @override
  56. void dispose() {
  57. _feedback?.dispose();
  58. _title?.dispose();
  59. super.dispose();
  60. }
  61. /// 选择图片
  62. void _onAddImage() async {
  63. if (_images.length >= 4) {
  64. Fluttertoast.showToast(msg: '最多上传4张图片');
  65. return;
  66. }
  67. var status = await Permission.photos.status;
  68. if (status != PermissionStatus.granted) {
  69. status = await Permission.photos.request();
  70. }
  71. if (status == PermissionStatus.denied) {
  72. Fluttertoast.showToast(msg: '暂无权限,图片选择失败');
  73. return null;
  74. }
  75. final picker = ImagePicker();
  76. PickedFile file;
  77. int index = await showModalBottomSheet(
  78. context: context,
  79. builder: (context) {
  80. return ActionSelectedAlert(
  81. // title: '拍照/选择图片',
  82. actions: ['拍照', '从相册选择图片'],
  83. );
  84. },
  85. isScrollControlled: false,
  86. backgroundColor: Colors.transparent);
  87. if (index != null) {
  88. if (index == 0) {
  89. file = await picker.getImage(source: ImageSource.camera);
  90. } else {
  91. file = await picker.getImage(source: ImageSource.gallery);
  92. }
  93. if (file == null) return;
  94. setState(() {
  95. _images.add(File(file.path));
  96. });
  97. // File resultFile = await EncodeUtil.compressImage(file, 800);
  98. }
  99. }
  100. @override
  101. Widget build(BuildContext context) {
  102. return BlocConsumer<FeedbackBloc, FeedbackState>(
  103. listener: (context, state) {},
  104. buildWhen: (prev, current) {
  105. return true;
  106. },
  107. builder: (context, state) {
  108. if (state is FeedbackLoadedState) {
  109. return _getMainWidget(state?.model);
  110. }
  111. return _getEmptyWidget();
  112. },
  113. );
  114. }
  115. /// 有数据
  116. Widget _getMainWidget(FeedbackModel model) {
  117. return Scaffold(
  118. resizeToAvoidBottomInset: false,
  119. appBar: _createNav(model),
  120. body: GestureDetector(
  121. onTap: () {
  122. FocusScope.of(context).requestFocus(FocusNode());
  123. },
  124. child: SafeArea(
  125. child: Column(
  126. children: <Widget>[
  127. Expanded(
  128. child: SingleChildScrollView(
  129. child: Column(
  130. children: <Widget>[
  131. Container(
  132. width: double.infinity,
  133. margin: EdgeInsets.only(top: 10, bottom: 10, left: 12.5, right: 12.5),
  134. padding: EdgeInsets.all(12.5),
  135. decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(7.5)),
  136. child: Column(
  137. crossAxisAlignment: CrossAxisAlignment.start,
  138. mainAxisAlignment: MainAxisAlignment.start,
  139. children: <Widget>[
  140. _createTitle(model),
  141. _createUpload(model),
  142. _createSubmit(model),
  143. ],
  144. ),
  145. )
  146. ],
  147. ),
  148. ),
  149. ),
  150. Center(
  151. child: GestureDetector(
  152. behavior: HitTestBehavior.opaque,
  153. onTap: () {
  154. Navigator.push(
  155. context,
  156. CupertinoPageRoute(
  157. builder: (_) => FeedbackRecordPage(),
  158. ),
  159. );
  160. },
  161. child: Container(
  162. width: 120,
  163. height: 30,
  164. margin: EdgeInsets.all(10),
  165. decoration: BoxDecoration(
  166. color: HexColor.fromHex(model?.feedbackListBtnBgColor ?? '#FFFFFF'),
  167. borderRadius: BorderRadius.circular(15),
  168. border: Border.all(color: HexColor.fromHex(model?.feedbackListBtnBorderColor ?? '#D1D1D1'), width: 0.5),
  169. ),
  170. child: Row(
  171. mainAxisAlignment: MainAxisAlignment.center,
  172. crossAxisAlignment: CrossAxisAlignment.center,
  173. children: <Widget>[
  174. Padding(
  175. padding: EdgeInsets.only(right: 6),
  176. child: CachedNetworkImage(
  177. imageUrl: model?.feedbackListBtnIcon ?? '',
  178. width: 16.5,
  179. ),
  180. ),
  181. Text(
  182. model?.feedbackListBtnText ?? '反馈记录',
  183. style: TextStyle(fontSize: 13, color: HexColor.fromHex(model?.feedbackListBtnTextColor ?? '#333333'), fontWeight: FontWeight.bold),
  184. )
  185. ],
  186. ),
  187. ),
  188. ),
  189. ),
  190. const SizedBox(height: 25),
  191. ],
  192. ),
  193. ),
  194. ),
  195. );
  196. }
  197. /// 没数据
  198. Widget _getEmptyWidget() {
  199. return Scaffold(
  200. resizeToAvoidBottomInset: false,
  201. appBar: _createNav(null),
  202. body: Container(),
  203. );
  204. }
  205. /// 导航栏
  206. Widget _createNav(FeedbackModel mode) {
  207. return CupertinoNavigationBar(
  208. border: Border(
  209. bottom: BorderSide(
  210. width: 0.0, // One physical pixel.
  211. style: BorderStyle.none,
  212. ),
  213. ),
  214. backgroundColor: HexColor.fromHex(mode?.appBarBgColor ?? '#FFFFFF'),
  215. leading: Navigator.canPop(context)
  216. ? GestureDetector(
  217. child: Container(
  218. padding: EdgeInsets.zero,
  219. child: Icon(
  220. Icons.arrow_back_ios,
  221. size: 20,
  222. ),
  223. ),
  224. onTap: () {
  225. if (Navigator.canPop(context)) {
  226. Navigator.pop(context);
  227. }
  228. },
  229. )
  230. : Container(),
  231. middle: Text(
  232. mode?.appBarName ?? '意见反馈',
  233. style: TextStyle(
  234. fontSize: 15,
  235. color: HexColor.fromHex(mode?.appBarNameColor ?? '#333333'),
  236. ),
  237. ),
  238. );
  239. }
  240. Widget _createTitle(FeedbackModel model) {
  241. return Column(
  242. crossAxisAlignment: CrossAxisAlignment.start,
  243. mainAxisAlignment: MainAxisAlignment.start,
  244. children: <Widget>[
  245. Text(
  246. model?.feedbackTitle ?? '反馈描述',
  247. style: TextStyle(
  248. fontSize: 14,
  249. color: Color(0xff333333),
  250. fontWeight: FontWeight.bold,
  251. ),
  252. ),
  253. const SizedBox(height: 2),
  254. /// 异常选项
  255. Padding(
  256. padding: EdgeInsets.only(top: 4, bottom: 4),
  257. child: Visibility(
  258. visible: !EmptyUtil.isEmpty(model?.feedbackTypes),
  259. child: Row(
  260. children: model.feedbackTypes
  261. .map((e) => Expanded(
  262. child: GestureDetector(
  263. behavior: HitTestBehavior.opaque,
  264. onTap: (){
  265. setState(() {
  266. model.feedbackTypes.forEach((element) {
  267. element.isSelect = false;
  268. });
  269. e.isSelect = true;
  270. });
  271. },
  272. child: Container(
  273. margin: EdgeInsets.only(left: 2, right: 2),
  274. height: 28,
  275. child: Container(
  276. decoration: BoxDecoration(
  277. borderRadius: BorderRadius.circular(2.5),
  278. border: Border.all(
  279. color: HexColor.fromHex(e.isSelect ? model?.feedbackTypeSelectedBorderColor : model?.feedbackTypeNoSelectedBorderColor),
  280. //e.isSelect ? Colors.redAccent : Color(0xffe7e7e7),
  281. width: 1,
  282. ),
  283. ),
  284. child: Stack(
  285. children: <Widget>[
  286. Center(
  287. child: Text(
  288. e.name ?? '',
  289. style: TextStyle(
  290. fontSize: 12,
  291. color: HexColor.fromHex(e.isSelect
  292. ? model?.feedbackTypeSelectedTextColor
  293. : model?.feedbackTypeNoSelectedTextColor) //e.isSelect ? Colors.redAccent : Color(0xff999999),
  294. ),
  295. ),
  296. ),
  297. Visibility(
  298. visible: e.isSelect,
  299. child: Align(
  300. alignment: Alignment.bottomRight,
  301. child: CachedNetworkImage(
  302. imageUrl: model?.feedbackTypeSelectedBorderIcon,
  303. width: 10,
  304. ),
  305. ),
  306. )
  307. ],
  308. ),
  309. )),
  310. )))
  311. .toList(),
  312. ),
  313. ),
  314. ),
  315. // 标题
  316. Container(
  317. height: 32.5,
  318. margin: EdgeInsets.only(left: 2, right: 2, top: 4, bottom: 4),
  319. child: CupertinoTextField(
  320. controller: _title,
  321. textAlignVertical: TextAlignVertical.center,
  322. style: TextStyle(fontSize: 12, color: Color(0xff333333)),
  323. maxLines: 1,
  324. placeholder: model?.feedbackInputHintText ?? '请输入反馈标题(最多15个字)',
  325. placeholderStyle: TextStyle(
  326. fontSize: 12,
  327. color: HexColor.fromHex(model?.feedbackInputHintTextColor ?? '#999999'),
  328. ),
  329. decoration: BoxDecoration(color: Color(0xfff9f9f9), borderRadius: BorderRadius.circular(7.5)),
  330. onChanged: (value) {
  331. if (value == null || value == '' || EmptyUtil.isEmpty(_feedback?.text?.toString()?.trim())) {
  332. _submitable = false;
  333. } else {
  334. _submitable = true;
  335. }
  336. setState(() {});
  337. },
  338. ),
  339. ),
  340. // 内容
  341. Container(
  342. height: 118,
  343. margin: EdgeInsets.only(left: 2, right: 2, top: 4, bottom: 4),
  344. child: CupertinoTextField(
  345. controller: _feedback,
  346. textAlignVertical: TextAlignVertical.top,
  347. style: TextStyle(fontSize: 12, color: Color(0xff333333)),
  348. maxLines: null,
  349. placeholder: model?.feedbackInputContentHintText ?? '请输入您的意见(最多100个字)',
  350. placeholderStyle: TextStyle(
  351. fontSize: 12,
  352. color: HexColor.fromHex(model?.feedbackInputContentHintTextColor ?? '#999999'),
  353. ),
  354. decoration: BoxDecoration(color: Color(0xfff9f9f9), borderRadius: BorderRadius.circular(7.5)),
  355. onChanged: (value) {
  356. if (value == null || value == '' || EmptyUtil.isEmpty(_title?.text?.toString()?.trim())) {
  357. _submitable = false;
  358. } else {
  359. _submitable = true;
  360. }
  361. setState(() {});
  362. },
  363. ),
  364. ),
  365. ],
  366. );
  367. }
  368. Widget _createUpload(FeedbackModel model) {
  369. List<Widget> images = List();
  370. _images.forEach((file) {
  371. images.add(Container(
  372. margin: EdgeInsets.only(top: 4, bottom: 4, right: 8),
  373. width: 80,
  374. height: 80,
  375. child: FeedbackImageWidget(
  376. file: file,
  377. onDelete: () {
  378. _images.remove(file);
  379. setState(() {});
  380. },
  381. ),
  382. ));
  383. });
  384. if (images.length < 4) {
  385. images.add(GestureDetector(
  386. child: Container(
  387. margin: EdgeInsets.only(top: 4, bottom: 4),
  388. decoration: BoxDecoration(borderRadius: BorderRadius.circular(7.5), color: Color(0xfff9f9f9)),
  389. height: 80,
  390. width: 80,
  391. child: Icon(
  392. Icons.add,
  393. size: 60,
  394. color: Color(0xffd8d8d8),
  395. ),
  396. ),
  397. onTap: _onAddImage,
  398. ));
  399. }
  400. return Column(
  401. children: <Widget>[
  402. Row(
  403. children: <Widget>[
  404. Padding(
  405. padding: EdgeInsets.only(right: 10, top: 4, bottom: 4),
  406. child: Text(
  407. model?.feedbackUploadImageTitle ?? '上传图片',
  408. style: TextStyle(
  409. fontSize: 14,
  410. color: Color(0xff333333),
  411. fontWeight: FontWeight.bold,
  412. ),
  413. ),
  414. ),
  415. Text(
  416. model?.feedbackUploadImageSubtitle ?? '最多上传4张,大小不超过1M/张',
  417. style: TextStyle(
  418. fontSize: 12,
  419. color: Color(0xff999999),
  420. ),
  421. ),
  422. ],
  423. ),
  424. Row(
  425. children: images,
  426. )
  427. ],
  428. );
  429. }
  430. Widget _createSubmit(FeedbackModel model) {
  431. return GestureDetector(
  432. onTap: () {
  433. if (!_submitable) {
  434. return;
  435. }
  436. Logger.debug('提交:${_feedback.text.toString()}');
  437. Fluttertoast.showToast(msg: '提交成功~');
  438. Navigator.pop(context);
  439. },
  440. child: Container(
  441. margin: EdgeInsets.only(top: 24, bottom: 4),
  442. height: 45,
  443. decoration: BoxDecoration(
  444. color: HexColor.fromHex(_submitable ? model?.feedbackTypeSelectedBorderColor ?? '' : model?.feedbackPostBtnBgColor ?? ''),
  445. borderRadius: BorderRadius.circular(7.5),
  446. ),
  447. child: Center(
  448. child: Text(
  449. model?.feedbackPostBtnText ?? '提交意见',
  450. style: TextStyle(
  451. fontSize: 14,
  452. color: HexColor.fromHex(model?.feedbackPostBtnTextColor ?? '#FFFFFF'),
  453. ),
  454. ),
  455. ),
  456. ),
  457. );
  458. }
  459. }