import 'dart:convert'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:image_picker/image_picker.dart'; import 'package:twongCustomer/routes/utils.dart'; import 'package:twongCustomer/utils/loading.dart'; import '../utils/http.dart'; import '../utils/cache.dart'; import '../utils/emoji.dart'; import '../models/index.dart'; import '../utils/events.dart'; import '../utils/size_fit.dart'; class ChatPage extends StatefulWidget{ final UserInfo user; ChatPage(this.user, {Key key}): super(key: key); @override State createState() { return _ChatPageState(); } } class _ChatPageState extends State with WidgetsBindingObserver { UserInfo user; FocusNode _inputFocus = new FocusNode(); List> emojis = new List>(); List _messageList = new List(); ScrollController _scrollController = new ScrollController(); TextEditingController _textController = new TextEditingController(); @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); initData(); initEventListener(); Events.fire(ClearUnread(widget.user.uid)); } @override void didChangeAppLifecycleState(AppLifecycleState state) { print("--" + state.toString()); switch (state) { case AppLifecycleState.inactive: // 处于这种状态的应用程序应该假设它们可能在任何时候暂停。 break; case AppLifecycleState.resumed:// 应用程序可见,前台 break; case AppLifecycleState.paused: // 应用程序不可见,后台 break; case AppLifecycleState.detached: break; } } @override void dispose() { super.dispose(); print("chat dispose"); } void initData() { initEmojis(); user = AppData.get("info"); // Loading.show(context); Http.getHistory(widget.user.uid).then((list) { if (!mounted) return; setState(() { _messageList = list; }); Loading.hide(context); }).catchError((err) { Loading.hide(context); Fluttertoast.showToast(msg: "获取聊天记录失败"); print("history err: " + err.toString()); }); } void initEmojis() { var idx = 1; var page = 0; emojis.add(new List()); for(var item in EmojiConf.list) { emojis[page].add(item); if(idx % 24 == 0){ page++; emojis.add(new List()); } idx++; } } void initEventListener() { Events.on().listen((event) { if(!mounted) return; setState(() { _messageList.add(event.message); _scrollController.animateTo(0, duration: const Duration(milliseconds: 300), curve: Curves.easeOut, ); }); }); } @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: true, appBar: AppBar( centerTitle: true, title: Text(widget.user.username), actions: [ IconButton(icon: Icon(Icons.more_horiz_outlined, size: 34), onPressed: () { RouterUtils.toUserInfo(context, widget.user); }), Container(width: 6) ], ), body: SafeArea( child: ListView(children: [ Container( height: 500, child: ListView.builder( reverse: true, controller: _scrollController, physics: BouncingScrollPhysics(), itemCount: _messageList.length, itemBuilder: (context, i) { return _buildMessage(i); }, ), ), // Divider(height: 1.0), _buildComposer(), ]), ) ); } Widget _buildAvatar(String url) { return Container( padding: EdgeInsets.only(top: 4), child: ClipRRect( borderRadius: BorderRadius.circular(20), child: Image.network(url, width: 40, height: 40) )); } Widget _buildMsgBody(bool visitor, HttpMessage msg) { // print("build msg: " + user.toJson().toString()); return Flexible(child: Container( margin: EdgeInsets.only(left: 10, right: 10), child: Column( crossAxisAlignment: visitor ? CrossAxisAlignment.start : CrossAxisAlignment .end, children: [ Text(visitor ? msg.visitor_name : user.name, textAlign: TextAlign.right,), Container( margin: EdgeInsets.only(top: 6), padding: EdgeInsets.all(10), decoration: BoxDecoration( color: _getBackground(visitor, msg), borderRadius: BorderRadius.all(Radius.circular(20)) ), child: _buildMsgContent(msg) ) ], ), )); } Color _getBackground(bool visitor, HttpMessage msg) { if(msg.content.startsWith("face[")) { return visitor ? Colors.cyanAccent : Colors.greenAccent; } if(msg.content.startsWith("img[")) { return null; } if(msg.content.startsWith("product[")) { return Colors.white70; } if(msg.content.startsWith("order[")) { return null; } return visitor ? Colors.cyanAccent : Colors.greenAccent; } Widget _buildMsgContent(HttpMessage msg) { if(msg.content.startsWith("face[")) { var name = msg.content.substring(5, msg.content.length - 1); return Container( height: 32, width: 32, child: Image.asset("assets/emojis/" + name + ".png")); } if(msg.content.startsWith("img[")) { var url = msg.content.substring(4, msg.content.length - 1); return Image.network(url); } if(msg.content.startsWith("product[")) { var url = msg.content.substring(8, msg.content.length - 1); print(url); var product = Product.fromJson(json.decode(url)); return _buildProduct(product); } if(msg.content.startsWith("order[")) { var url = msg.content.substring(6, msg.content.length - 1); print(url); var order = Order.fromJson(json.decode(url)); return _buildOrder(order); } return Text(msg.content); } Widget _buildOrder(Order order) { return Container( padding: EdgeInsets.all(10.px), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(4.px) ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("订单号:"), Row( children: [ Expanded(child: Text(order.order_id, style: TextStyle(decoration: TextDecoration.underline, fontWeight: FontWeight.bold))), GestureDetector( onTap: () { Clipboard.setData(ClipboardData(text: order.order_id)) .then((value) => Fluttertoast.showToast(msg: "复制成功!")); }, child: Container( padding: EdgeInsets.only(left: 8.px,right: 8.px, top: 2.px, bottom: 2.px), decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(4.px) ), child: Text("复制", style: TextStyle(color: Colors.white)), ), ) ], ) ], )); } Widget _buildProduct(Product product) { return Container( padding: EdgeInsets.all(4.px), child: Column( children: [ Image.network(product.image), Text(product.store_name, style: TextStyle(fontWeight: FontWeight.bold)), Row( children: [ Expanded(child: RichText( text: TextSpan( text: "会员价:", style: TextStyle(color: Colors.black, fontSize: 14.px), children: [ TextSpan( text: "¥", style: TextStyle(color: Colors.black, fontSize: 12.px), ), TextSpan( text: product.vip_price, style: TextStyle(color: Colors.red, fontSize: 16.px) ) ] ), )), Expanded(child: RichText( text: TextSpan( text: "售价:", style: TextStyle(color: Colors.black, fontSize: 14.px), children: [ TextSpan( text: "¥", style: TextStyle(color: Colors.black, fontSize: 12.px), ), TextSpan( text: product.price, style: TextStyle(color: Colors.red, fontSize: 16.px) ) ] ), )), ], ) ], ), ); } Widget _buildMessage(idx) { var msg = _messageList[_messageList.length - idx - 1]; var visitor = msg.mes_type == "visitor"; var customer = AppData.get("info") as UserInfo; // print(msg.toJson()); return Container( padding: EdgeInsets.all(10), alignment: visitor ? Alignment.topLeft : Alignment.topRight, child: visitor ? Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ _buildAvatar(visitor ? msg.visitor_avator : customer.avator), _buildMsgBody(visitor, msg) ], ): Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.end, children: [ _buildMsgBody(visitor, msg), _buildAvatar(customer.avator) ], ) ); } Widget _buildComposer() { return Container( alignment: Alignment.center, height: 45.0, width: double.infinity, margin: EdgeInsets.all(3.0), child: Row( children: [ IconButton(icon: Icon( Icons.emoji_emotions, color: Colors.black26, size: 30,), onPressed: _openFaces), Expanded( child: TextField( focusNode: _inputFocus, textInputAction: TextInputAction.send, controller: _textController, onSubmitted: _submitMsg, decoration: InputDecoration( contentPadding: EdgeInsets.all(10.0), hintText: "想说点儿什么?", fillColor: Colors.white, filled: true, border: OutlineInputBorder( borderSide: BorderSide.none, borderRadius: BorderRadius.all(Radius.circular(5)), ), ), ), ), IconButton( icon: Icon(Icons.add_circle_outline), onPressed: _openAction, color: Colors.black26, ), ], ), ); } void _submitMsg(String txt) async { var tem = _textController.text; _textController.text = ""; FocusScope.of(context).requestFocus(_inputFocus); await Http.sendMsg(widget.user.uid, txt).then((res) { var msg = HttpMessage(); msg.content = txt; Events.fire(UserMessage(msg)); }).catchError((err) { print(err); _textController.text = tem; Fluttertoast.showToast(msg: "发送失败!"); }); } void _submitFace(String name) async { var code = "face[$name]"; await Http.sendMsg(widget.user.uid, code).then((res) { var msg = HttpMessage(); msg.content = code; Events.fire(UserMessage(msg)); Navigator.pop(context); }).catchError((err) { print(err); Fluttertoast.showToast(msg: "发送失败!"); }); } Widget _getAvatar(String name) { return InkWell(onTap: () => _submitFace(name), child: CircleAvatar(child: Container(child: Image.asset("assets/emojis/" + name + ".png"), margin: EdgeInsets.all(8)), radius: 20, backgroundColor: Colors.transparent)); } Future _openFaces () { var pageWidgets = List(); for(var item in emojis) { var faceWidgets = List(); for(var face in item) { faceWidgets.add(_getAvatar(face)); } pageWidgets.add(GridView( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 8, //横轴三个子widget childAspectRatio: 1 //宽高比为1时,子widget ), children: faceWidgets, )); } return showModalBottomSheet( context: context, isScrollControlled: true, builder: (BuildContext context) { return Container( height: 160, child: PageView( children: pageWidgets, ), ); }); } Future _openAction() { return showModalBottomSheet( context: context, builder: (BuildContext context) { return SafeArea(child: Column( mainAxisSize: MainAxisSize.min, children: [ new ListTile( leading: Icon(Icons.photo_camera), title: Text("相机拍摄"), onTap: () async { var image = await ImagePicker.pickImage( source: ImageSource.camera); Navigator.pop(context); }, ), new ListTile( leading: Icon(Icons.photo_library), title: Text("照片库"), onTap: () async { var image = await ImagePicker.pickImage( source: ImageSource.gallery); Navigator.pop(context); }, ), ], )); }); } }