欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-30 08:35:26
...

       本来打算开启下一段作死旅行的,后来想了想还是先把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();
}

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

flutter 封装评论回复弹出键盘输入框组件
图片给我整的大大的,都好好欣赏下我大幂幂。
客人来了,服务肯定要做全套,舒舒服服的。

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

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