customerChat.dart 13 KB

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