customerChat.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. import 'package:flutter/services.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/cupertino.dart';
  4. import 'package:bot_toast/bot_toast.dart';
  5. import 'package:image_picker/image_picker.dart';
  6. import '../../utils/index.dart';
  7. import '../../config/emoji.dart';
  8. import 'package:twong/widgets/app_bar.dart';
  9. class ChatPage extends StatefulWidget{
  10. ChatPage({Key key}): super(key: key);
  11. @override
  12. State<StatefulWidget> createState() {
  13. return _ChatPageState();
  14. }
  15. }
  16. class _ChatPageState extends State<ChatPage> {
  17. ImagePicker _picker = new ImagePicker();
  18. FocusNode _inputFocus = new FocusNode();
  19. List<dynamic> _messageList = new List<dynamic>();
  20. List<List<String>> emojis = new List<List<String>>();
  21. ScrollController _scrollController = new ScrollController();
  22. TextEditingController _textController = new TextEditingController();
  23. @override
  24. void initState() {
  25. super.initState();
  26. initData();
  27. initEventListener();
  28. }
  29. @override
  30. void dispose() {
  31. super.dispose();
  32. print("chat dispose");
  33. }
  34. void initData() {
  35. initEmojis();
  36. // Http.getHistory(widget.user.uid).then((list) {
  37. // setState(() {
  38. // _messageList = list;
  39. // });
  40. // }).catchError((err){
  41. // print("history err: " + err.toString());
  42. // });
  43. }
  44. void initEmojis() {
  45. var idx = 1;
  46. var page = 0;
  47. emojis.add(new List<String>());
  48. for(var item in EmojiConf.list) {
  49. emojis[page].add(item);
  50. if(idx % 24 == 0){
  51. page++;
  52. emojis.add(new List<String>());
  53. }
  54. idx++;
  55. }
  56. }
  57. void initEventListener() {
  58. // Events.on<UserMessage>().listen((event) {
  59. // if(!mounted) return;
  60. // setState(() {
  61. // _messageList.add(event.message);
  62. // _scrollController.animateTo(0,
  63. // duration: const Duration(milliseconds: 300),
  64. // curve: Curves.easeOut,
  65. // );
  66. // });
  67. // });
  68. }
  69. @override
  70. Widget build(BuildContext context) {
  71. return Scaffold(
  72. appBar: DAppBar("客服名字"),
  73. body: Column(children: <Widget>[
  74. new Flexible(
  75. child: new ListView.builder(
  76. reverse: true,
  77. controller: _scrollController,
  78. physics: BouncingScrollPhysics(),
  79. itemCount: _messageList.length,
  80. itemBuilder: (context, i) {
  81. return _buildMessage(i);
  82. },
  83. ),
  84. ),
  85. new Divider(height: 1.0),
  86. new Container(
  87. child: _buildComposer(),
  88. decoration:
  89. new BoxDecoration(color: Color.fromRGBO(241, 243, 244, 0.9)),
  90. ),
  91. ]),
  92. );
  93. }
  94. Widget _buildAvatar(String url) {
  95. return Container(
  96. padding: EdgeInsets.only(top: 4),
  97. child: ClipRRect(
  98. borderRadius: BorderRadius.circular(20.px),
  99. child: Image.network(url, width: 40.px, height: 40.px)
  100. ));
  101. }
  102. Widget _buildMsgBody(bool visitor, dynamic msg) {
  103. // return Flexible(child: Container(
  104. // margin: EdgeInsets.only(left: 10, right: 10),
  105. // child: Column(
  106. // crossAxisAlignment: visitor ? CrossAxisAlignment.start : CrossAxisAlignment
  107. // .end,
  108. // children: [
  109. // Text(visitor ? msg.visitor_name : user.name, textAlign: TextAlign.right,),
  110. // Container(
  111. // margin: EdgeInsets.only(top: 6),
  112. // padding: EdgeInsets.all(10),
  113. // decoration: BoxDecoration(
  114. // color: _getBackground(visitor, msg),
  115. // borderRadius: BorderRadius.all(Radius.circular(20))
  116. // ),
  117. // child: _buildMsgContent(msg)
  118. // )
  119. // ],
  120. // ),
  121. // ));
  122. }
  123. Color _getBackground(bool visitor, dynamic msg) {
  124. if(msg.content.startsWith("face[")) {
  125. return visitor ? Colors.cyanAccent : Colors.greenAccent;
  126. }
  127. if(msg.content.startsWith("img[")) {
  128. return null;
  129. }
  130. if(msg.content.startsWith("product[")) {
  131. return Colors.white70;
  132. }
  133. if(msg.content.startsWith("order[")) {
  134. return null;
  135. }
  136. return visitor ? Colors.cyanAccent : Colors.greenAccent;
  137. }
  138. Widget _buildMsgContent(dynamic msg) {
  139. // if(msg.content.startsWith("face[")) {
  140. // var name = msg.content.substring(5, msg.content.length - 1);
  141. // return Container( height: 32, width: 32, child: Image.asset("assets/emojis/" + name + ".png"));
  142. // }
  143. //
  144. // if(msg.content.startsWith("img[")) {
  145. // var url = msg.content.substring(4, msg.content.length - 1);
  146. // return Image.network(url);
  147. // }
  148. //
  149. // if(msg.content.startsWith("product[")) {
  150. // var url = msg.content.substring(8, msg.content.length - 1);
  151. // print(url);
  152. // var product = Product.fromJson(json.decode(url));
  153. // return _buildProduct(product);
  154. // }
  155. //
  156. // if(msg.content.startsWith("order[")) {
  157. // var url = msg.content.substring(6, msg.content.length - 1);
  158. // print(url);
  159. // var order = Order.fromJson(json.decode(url));
  160. // return _buildOrder(order);
  161. // }
  162. //
  163. // return Text(msg.content);
  164. }
  165. Widget _buildOrder(dynamic order) {
  166. return Container(
  167. padding: EdgeInsets.all(10.px),
  168. decoration: BoxDecoration(
  169. color: Colors.white,
  170. borderRadius: BorderRadius.circular(4.px)
  171. ),
  172. child: Column(
  173. crossAxisAlignment: CrossAxisAlignment.start,
  174. children: [
  175. Text("订单号:"),
  176. Row(
  177. children: [
  178. Expanded(child: Text(order.order_id, style: TextStyle(decoration:
  179. TextDecoration.underline, fontWeight: FontWeight.bold))),
  180. GestureDetector(
  181. onTap: () { Utils.copyText(order.order_id); },
  182. child: Container(
  183. padding: EdgeInsets.only(left: 8.px,right: 8.px, top: 2.px, bottom: 2.px),
  184. decoration: BoxDecoration(
  185. color: Colors.blue,
  186. borderRadius: BorderRadius.circular(4.px)
  187. ),
  188. child: Text("复制", style: TextStyle(color: Colors.white)),
  189. ),
  190. )
  191. ],
  192. )
  193. ],
  194. ));
  195. }
  196. Widget _buildProduct(dynamic product) {
  197. return Container(
  198. padding: EdgeInsets.all(4.px),
  199. child: Column(
  200. children: [
  201. Image.network(product.image),
  202. Text(product.store_name, style: TextStyle(fontWeight: FontWeight.bold)),
  203. Row(
  204. children: [
  205. Expanded(child: RichText(
  206. text: TextSpan(
  207. text: "会员价:",
  208. style: TextStyle(color: Colors.black, fontSize: 14.px),
  209. children: [
  210. TextSpan(
  211. text: "¥",
  212. style: TextStyle(color: Colors.black, fontSize: 12.px),
  213. ),
  214. TextSpan(
  215. text: product.vip_price,
  216. style: TextStyle(color: Colors.red, fontSize: 16.px)
  217. )
  218. ]
  219. ),
  220. )),
  221. Expanded(child: RichText(
  222. text: TextSpan(
  223. text: "售价:",
  224. style: TextStyle(color: Colors.black, fontSize: 14.px),
  225. children: [
  226. TextSpan(
  227. text: "¥",
  228. style: TextStyle(color: Colors.black, fontSize: 12.px),
  229. ),
  230. TextSpan(
  231. text: product.price,
  232. style: TextStyle(color: Colors.red, fontSize: 16.px)
  233. )
  234. ]
  235. ),
  236. )),
  237. ],
  238. )
  239. ],
  240. ),
  241. );
  242. }
  243. Widget _buildMessage(idx) {
  244. var msg = _messageList[_messageList.length - idx - 1];
  245. // var visitor = msg.mes_type == "visitor";
  246. // var customer = AppData.get("info") as UserInfo;
  247. // return Container(
  248. // padding: EdgeInsets.all(10),
  249. // alignment: visitor ? Alignment.topLeft : Alignment.topRight,
  250. // child: visitor ? Row(
  251. // crossAxisAlignment: CrossAxisAlignment.start,
  252. // mainAxisAlignment: MainAxisAlignment.start,
  253. // children: [
  254. // _buildAvatar(visitor ? msg.visitor_avator : customer.avator),
  255. // _buildMsgBody(visitor, msg)
  256. // ],
  257. // ): Row(
  258. // crossAxisAlignment: CrossAxisAlignment.start,
  259. // mainAxisAlignment: MainAxisAlignment.end,
  260. // children: [
  261. // _buildMsgBody(visitor, msg),
  262. // _buildAvatar(customer.avator)
  263. // ],
  264. // )
  265. // );
  266. }
  267. Widget _buildComposer() {
  268. return new IconTheme(
  269. data: new IconThemeData(color: Color.fromRGBO(241, 243, 244, 0.9)),
  270. child: new Container(
  271. alignment: Alignment.center,
  272. height: 45.0,
  273. margin: const EdgeInsets.all(3.0),
  274. child: new Row(
  275. children: <Widget>[
  276. IconButton(icon: Icon(Icons.emoji_emotions, color: Colors.black26, size: 30,), onPressed: _openFaces),
  277. Flexible(
  278. child: TextField(
  279. focusNode: _inputFocus,
  280. textInputAction: TextInputAction.send,
  281. controller: _textController,
  282. onSubmitted: _submitMsg,
  283. decoration: InputDecoration(
  284. contentPadding: EdgeInsets.all(10.0),
  285. hintText: "想说点儿什么?",
  286. fillColor: Colors.white,
  287. filled: true,
  288. border: OutlineInputBorder(
  289. borderSide: BorderSide.none,
  290. borderRadius: BorderRadius.all(Radius.circular(5)),
  291. ),
  292. ),
  293. ),
  294. ),
  295. IconButton(
  296. icon: Icon(Icons.add_circle_outline),
  297. onPressed: _openAction,
  298. color: Colors.black26,
  299. ),
  300. ],
  301. ),
  302. ),
  303. );
  304. }
  305. void _submitMsg(String txt) async {
  306. FocusScope.of(context).requestFocus(_inputFocus);
  307. // await Http.sendMsg(widget.user.uid, txt).then((res) {
  308. // var msg = HttpMessage();
  309. // msg.content = txt;
  310. // Events.fire(UserMessage(msg));
  311. // _textController.text = "";
  312. // }).catchError((err) {
  313. // print(err);
  314. // Fluttertoast.showToast(msg: "发送失败!");
  315. // });
  316. }
  317. void _submitFace(String name) async {
  318. var code = "face[$name]";
  319. // await Http.sendMsg(widget.user.uid, code).then((res) {
  320. // var msg = HttpMessage();
  321. // msg.content = code;
  322. // Events.fire(UserMessage(msg));
  323. // Navigator.pop(context);
  324. // }).catchError((err) {
  325. // print(err);
  326. // Fluttertoast.showToast(msg: "发送失败!");
  327. // });
  328. }
  329. Widget _getAvatar(String name) {
  330. return InkWell(onTap: () => _submitFace(name), child: CircleAvatar(child:
  331. Container(child: Image.asset("assets/emojis/" + name + ".png"),
  332. margin: EdgeInsets.all(8)), radius: 20, backgroundColor: Colors.transparent));
  333. }
  334. Future _openFaces () {
  335. var pageWidgets = List<Widget>();
  336. for(var item in emojis) {
  337. var faceWidgets = List<Widget>();
  338. for(var face in item) {
  339. faceWidgets.add(_getAvatar(face));
  340. }
  341. pageWidgets.add(GridView(
  342. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  343. crossAxisCount: 8, //横轴三个子widget
  344. childAspectRatio: 1 //宽高比为1时,子widget
  345. ),
  346. children: faceWidgets,
  347. ));
  348. }
  349. return showModalBottomSheet(
  350. context: context,
  351. isScrollControlled: true,
  352. builder: (BuildContext context) {
  353. return Container(
  354. height: 160,
  355. child: PageView(
  356. children: pageWidgets,
  357. ),
  358. );
  359. });
  360. }
  361. Future _openAction() {
  362. return showModalBottomSheet(
  363. context: context,
  364. builder: (BuildContext context) {
  365. return Column(
  366. mainAxisSize: MainAxisSize.min,
  367. children: <Widget>[
  368. new ListTile(
  369. leading: Icon(Icons.photo_camera),
  370. title: Text("相机拍摄"),
  371. onTap: () async {
  372. var image = await _picker.getImage(
  373. source: ImageSource.camera);
  374. Navigator.pop(context);
  375. },
  376. ),
  377. new ListTile(
  378. leading: Icon(Icons.photo_library),
  379. title: Text("照片库"),
  380. onTap: () async {
  381. var image = await _picker.getImage(
  382. source: ImageSource.gallery);
  383. Navigator.pop(context);
  384. },
  385. ),
  386. ],
  387. );
  388. });
  389. }
  390. }