flutter 封装评论回复弹出键盘输入框组件

tech2022-10-17  124

       本来打算开启下一段作死旅行的,后来想了想还是先把flutter这段不堪回首的作死往事总结一下吧,好了,如之前的博文一样,不知不觉又开始废话连篇了。(ps:这么爱讲废话,不知道自己去日记)。        这次flutter重构之旅包含部分社区的功能,社区又怎么能少了正常的用户交流呢,所以一个评论回复而且能发图不发种,xx万人捅的发布组件呼之欲出。思来想去,似乎并没有想出更好的交互,只能学学你们口中不共戴天,水火不容的产品,所有的产品都是互相借鉴,于是我也去稍微借鉴借鉴目前流行的交互方式,无非就是点击弹出键盘和输入框,其他就没什么了。能不能设计得酷炫点,是看不起我们开发实现不了吗?        翻来翻去,手机上app都翻烂了,果然千篇一律,不亏为借鉴,可能借的人比较多哈。好了玩笑的话就不多说了,直接开始。

。。。。。。。。。思考再三,无从下手,此时的脸啪啪的疼。

搞错了,重来。

首先 先封装入口方法吧,用showDialog这个类往路由栈里插入一条记录,对于原生开发来说不比h5,原生开发所有的视图都是叠在一起的,只需要把背景搞透明就感觉像在一个页面一样,而h5往dom上插dom容易出事。总结:遇事不决跳页面。

void Function(BuildContext, Map<String, dynamic>, [dynamic]) comment = (BuildContext context, Map<String, dynamic> params, [dynamic callback]) { showDialog<Null>( context: context, //BuildContext对象 builder: (BuildContext context) { return GestureDetector( onTap: () { router.back(context); //点击背景透明层,退出弹出框 }, child: CommentDialog(params: params, callback: callback), ); }); };

然后 开始写输入框类,由于我这里需要用setData来更新视图,所以先提取CommentDialog继承与无状态StatelessWidget用作父组件,把有状态组件尽量封存在最小小部件CommentContent 里面。

class CommentDialog extends StatelessWidget { final dynamic callback; final Map<String, dynamic> params; CommentDialog({Key key, @required this.callback, this.params}) : assert(callback != null), super(key: key); @override Widget build(BuildContext context) { return Material( //创建透明层 type: MaterialType.transparency, //透明类型 child: GestureDetector( onTap: () { return false; }, child: Stack(children: <Widget>[ Positioned( left: 0, right: 0, bottom: MediaQuery.of(context).viewInsets.bottom > 0 ? MediaQuery.of(context).viewInsets.bottom : 0, child: Container( width: MediaQuery.of(context).size.width, padding: EdgeInsets.only( left: 16.0, right: 16.0, top: 10.0, bottom: 6.0), decoration: BoxDecoration(color: Colors.white), child: CommentContent( params: params, callback: callback, ))) ])), ); } } class CommentContentState extends State<CommentContent> { String text; String currentValue = ''; List<File> imgList = []; List<String> imgPaths = []; bool pending = false; @override void initState() { super.initState(); } addImage() { pickImageFromCameraOrAlbum(context, 3 - imgList.length, (PickImageResponse r) { if (r.files != null) { imgList.addAll(r.files); } if (r.paths != null) { imgPaths.addAll(r.paths); } setState(() {}); }); } comment() async { if (pending == true) return; pending = true; final Map<String, dynamic> params = widget.params; params['content'] = currentValue; // 先上传七牛云 if (imgPaths != null && imgPaths.length != 0) { final rr = await uploadQiNiu(imgPaths); if (rr == null || rr.length == 0) return; params['resource'] = json.encode(rr); } toast(context, '资源提交中,请勿重复点击或退出'); // 再评论 final r = await api.commentOrReply(params); pending = false; if (r.code == 1) { toast(context, '评论成功'); if (widget.callback != null) { widget.callback(); } } // 回复 router.back(context); } @override Widget build(BuildContext context) { return Column( children: <Widget>[ imgList.length == 0 ? Padding(padding: EdgeInsets.only(right: 0.0)) : Container( padding: EdgeInsets.only(bottom: 8.0), child: Row( children: imgList .map( (e) => Container( height: 80.0, width: 80.0, margin: EdgeInsets.only(right: 8.0), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(8.0)), image: DecorationImage( fit: BoxFit.cover, image: FileImage( e, ), ), color: Color(0xFFF0F0F0), ), child: GestureDetector( behavior: HitTestBehavior.opaque, child: Align( alignment: Alignment.topRight, child: Opacity( opacity: 0.3, child: Container( height: 13.0, width: 13.0, margin: EdgeInsets.all(4.0), padding: EdgeInsets.all(1.0), decoration: BoxDecoration( shape: BoxShape.circle, color: Color(0xff000000)), child: Image.asset( 'lib/images/cancle-image.png', height: 10.0, width: 10.0, ), ), )), onTap: () { imgList.remove(e); setState(() {}); }, ), ), ) .toList(), ), ), ConstrainedBox( constraints: BoxConstraints( minHeight: 40.0, ), child: Container( padding: EdgeInsets.only(left: 6.0, right: 6.0, bottom: 4.0), decoration: BoxDecoration(color: Color(0xfff0f0f0)), child: TextFormField( decoration: InputDecoration( hintText: "说点什么", border: InputBorder.none, ), style: TextStyle(fontSize: 14.0, color: Color(0xff606266)), autofocus: true, cursorColor: Color(0xff00c295), scrollPadding: EdgeInsets.only(top: 0.0, bottom: 6.0), minLines: 2, maxLines: 3, onChanged: (v) { currentValue = v; setState(() {}); }, ), ), ), Padding(padding: EdgeInsets.only(bottom: 4.0)), Row( children: <Widget>[ Container( height: 30.0, width: 32.0, child: FlatButton.icon( onPressed: addImage, padding: EdgeInsets.all(0.0), focusColor: Colors.white, hoverColor: Colors.white, highlightColor: Colors.white, splashColor: Colors.white, icon: Icon( Icons.image, color: Colors.grey, ), label: Text(''))), Expanded(child: Text('')), Container( height: 30.0, width: 40.0, child: FlatButton( padding: EdgeInsets.all(0.0), focusColor: Colors.white, hoverColor: Colors.white, highlightColor: Colors.white, splashColor: Colors.white, child: Text( '发布', style: TextStyle( color: Color(currentValue.length == 0 ? 0xffbcbcbc : 0xff00c295)), ), onPressed: () { if (currentValue == '') return; comment(); }, )) ], ) ], ); } } class CommentContent extends StatefulWidget { final dynamic callback; final Map<String, dynamic> params; CommentContent({Key key, @required this.callback, this.params}) : assert(callback != null), super(key: key); @override CommentContentState createState() => CommentContentState(); }

界面大概就是下面这个样子吧。

图片给我整的大大的,都好好欣赏下我大幂幂。 客人来了,服务肯定要做全套,舒舒服服的。

拍照+相册选择多张图,请看这一篇。 上传七牛云方法,请看这一篇。 仓库地址:传送门。

好了,废话也说完了,东西也实现了,广告也打完了,是时候说再见了。 难忘今宵,难忘~今宵。

最新回复(0)