【Flutter】自定义折叠组件

一、基础组件

import 'package:flutter/material.dart';

const Duration _kExpand = Duration(milliseconds: 200);

/* 折叠组件 */
class ExpansionCustom extends StatefulWidget {
  const ExpansionCustom({
    Key key,
    this.title,
    this.onExpansionChanged,
    this.children = const <Widget>[],
    this.initiallyExpanded = false,
    this.isExpanded,
    this.padding,
  })  : assert(initiallyExpanded != null),
        super(key: key);

  final Widget title;
  final ValueChanged<bool> onExpansionChanged;
  final List<Widget> children;
  final bool initiallyExpanded;
  final bool isExpanded;
  final EdgeInsetsGeometry padding;

  @override
  _ExpansionCustomState createState() => _ExpansionCustomState();
}

class _ExpansionCustomState extends State<ExpansionCustom>
    with SingleTickerProviderStateMixin {
  static final Animatable<double> _easeInTween =
      CurveTween(curve: Curves.easeIn);

  AnimationController _controller;
  Animation<double> _heightFactor;

  bool _isExpanded = false;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(duration: _kExpand, vsync: this);
    _heightFactor = _controller.drive(_easeInTween);

    _isExpanded =
        PageStorage.of(context)?.readState(context) ?? widget.initiallyExpanded;
    if (_isExpanded) _controller.value = 1.0;
  }

  diyHandle() {
    if (widget.isExpanded != null) {
      setState(() {
        if (widget.isExpanded) {
          _controller.forward();
        } else {
          _controller.reverse().then<void>((void value) {
            if (!mounted) return;
            setState(() {});
          });
        }
      });
    }
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _handleTap() {
    setState(() {
      _isExpanded = !_isExpanded;
      if (_isExpanded) {
        _controller.forward();
      } else {
        _controller.reverse().then<void>((void value) {
          if (!mounted) return;
          setState(() {});
        });
      }
      PageStorage.of(context)?.writeState(context, _isExpanded);
    });
    if (widget.onExpansionChanged != null)
      widget.onExpansionChanged(_isExpanded);
  }

  Widget _buildChildren(BuildContext context, Widget child) {
    return Container(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          GestureDetector(
            onTap: _handleTap,
            child: Container(
              color: Colors.transparent,
              width: double.infinity,
              padding: widget.padding ?? EdgeInsets.only(left: 10, right: 10),
              child: widget.title ?? Container(),
            ),
          ),
          ClipRect(
            child: Align(
              heightFactor: _heightFactor.value,
              child: child,
            ),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    diyHandle();
    final bool closed = !_isExpanded && _controller.isDismissed;
    return AnimatedBuilder(
      animation: _controller.view,
      builder: _buildChildren,
      child: closed ? null : Column(children: widget.children),
    );
  }
}

二、拓展组件

对基础组件加以封装,可以实现更多功能的拓展组件,本文以折叠卡片和折叠信息为例。

折叠卡片

import 'package:flutter/material.dart';
import './expansion_custom.dart';

/* 折叠卡片 */
class ExpansionCard extends StatefulWidget {
  final EdgeInsets margin; // 外边距
  final Decoration cardDecoration; // 卡片样式
  final Function onTap; // 点击标题的回调
  final String tlText; // 左侧标题
  final TextStyle tlStyle;
  final Widget tlWidget;
  final String trText; // 右侧标题
  final TextStyle trStyle;
  final Widget trWidget;
  final List<Widget> contentWidget; // 折叠内容

  ExpansionCard({
    Key key,
    this.margin,
    this.cardDecoration,
    this.onTap,
    this.tlText,
    this.tlStyle,
    this.tlWidget,
    this.trText,
    this.trStyle,
    this.trWidget,
    this.contentWidget,
  }) : super(key: key);

  @override
  _ExpansionCardState createState() => _ExpansionCardState();
}

class _ExpansionCardState extends State<ExpansionCard> {
  bool _isExpanded = false;

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: widget.margin ?? EdgeInsets.all(10),
      padding: EdgeInsets.all(10),
      decoration: widget.cardDecoration ??
          BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.all(Radius.circular(10)),
          ),
      child: Column(
        children: <Widget>[
          GestureDetector(
            onTap: () {
              this._isExpanded = !this._isExpanded;
              if (mounted) {
                setState(() {});
              }

              if (widget.onTap != null) {
                widget.onTap(this._isExpanded);
              }
            },
            child: Container(
              color: Colors.transparent,
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: <Widget>[
                  widget.tlWidget ??
                      Text(
                        '${widget.tlText ?? ""}',
                        style: widget.tlStyle ??
                            TextStyle(
                                fontSize: 16, fontWeight: FontWeight.bold),
                      ),
                  Row(
                    children: <Widget>[
                      widget.trWidget ??
                          Text('${widget.trText ?? ""}',
                              style: widget.trStyle ??
                                  TextStyle(
                                      fontSize: 16,
                                      fontWeight: FontWeight.bold)),
                      SizedBox(width: 4),
                      Icon(
                        this._isExpanded ? Icons.remove : Icons.add,
                        color: Colors.black,
                        size: 18,
                      ),
                    ],
                  )
                ],
              ),
            ),
          ),
          ExpansionCustom(
            children: <Widget>[
              Divider(height: 10),
              Column(
                children: widget.contentWidget ?? [Container()],
              )
            ],
            initiallyExpanded: false,
            isExpanded: this._isExpanded,
          )
        ],
      ),
    );
  }
}

折叠信息

import 'package:flutter/material.dart';
import './expansion_custom.dart';

/* 折叠消息 */
class ExpansionMsg extends StatefulWidget {
  final double width; //宽
  final Color bgColor; //背景颜色
  final Widget icon;//左侧icon
  final String msgText; //文字提示
  final TextStyle msgTextStyle; //文字提示样式
  final Function onClose; //点击关闭时的回调
  ExpansionMsg(
      {Key key,
      this.width,
      this.bgColor,
      this.icon,
      this.msgText,
      this.msgTextStyle,
      this.onClose})
      : super(key: key);

  @override
  _ExpansionMsgState createState() => _ExpansionMsgState();
}

class _ExpansionMsgState extends State<ExpansionMsg> {
  bool _showContent = true;

  @override
  Widget build(BuildContext context) {
    return ExpansionCustom(
      title: Container(),
      initiallyExpanded: true, //默认是否展开
      isExpanded: this._showContent,
      children: <Widget>[
        MessageBox(
          width: widget.width,
          bgColor: widget.bgColor,
          msgTextStyle: widget.msgTextStyle,
          msgText: widget.msgText,
          canClose: true,
          onClose: () {
            this._showContent = false;

            if (mounted) {
              setState(() {});
            }

            if (widget.onClose != null) {
              widget.onClose();
            }
          },
        ),
      ],
    );
  }
}

/* 消息盒子 */
class MessageBox extends StatelessWidget {
  final double width;
  final Color bgColor;
  final Widget icon;
  final String msgText;
  final TextStyle msgTextStyle;
  final Function onClose;
  final bool canClose;
  const MessageBox(
      {Key key,
      this.width,
      this.bgColor,
      this.icon,
      this.msgText,
      this.msgTextStyle,
      this.onClose,
      this.canClose = true})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.only(
        top: 6,
        bottom: 6,
        left: 10,
        right: 10,
      ),
      width: width ?? double.infinity,
      color: bgColor ?? Color.fromRGBO(253, 246, 236, 1),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Container(
            width: 20,
            margin: EdgeInsets.only(right: 10),
            child: icon ??
                Icon(Icons.warning,
                    size: 20, color: Color.fromRGBO(230, 162, 60, 1)),
          ),
          Expanded(
              child: Container(
            child: Text(
              msgText ??
                  '这是一条消息提示',
              style: msgTextStyle ??
                  TextStyle(
                      fontSize: 12, color: Color.fromRGBO(230, 162, 60, 1)),
            ),
          )),
          canClose
              ? GestureDetector(
                  onTap: onClose,
                  child: Container(
                    color: Colors.transparent,
                    width: 20,
                    height: 20,
                    child: Icon(
                      Icons.close,
                      size: 20,
                    ),
                  ),
                )
              : Container(width: 20)
        ],
      ),
    );
  }
}

三、组件使用

import 'package:flutter/material.dart';
import '../../compontents/expansion/expansion_custom.dart';
import '../../compontents/expansion/expansion_card.dart';
import '../../compontents/expansion/expansion_msg.dart';

/*组件演示*/
class Demo extends StatefulWidget {
  Demo({Key key}) : super(key: key);

  @override
  State<Demo> createState() => _DemoState();
}

class _DemoState extends State<Demo> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Demo'),
          centerTitle: true,
        ),
        body: Container(
          child: Wrap(
              spacing: 20,
              crossAxisAlignment: WrapCrossAlignment.center,
              children: <Widget>[
                SizedBox(height: 10),
                ExpansionCustom(
                  title: Container(
                      child: Text('标题'), alignment: Alignment.centerLeft),
                  children: <Widget>[Text('内容1'), Text('内容2'), Text('内容3')],
                ),
                SizedBox(height: 10),
                ExpansionCard(
                  tlText: '左侧标题',
                  trText: '右侧标题',
                  contentWidget: <Widget>[
                    Text('折叠内容1'),
                    Text('折叠内容2'),
                    Text('折叠内容3'),
                    Text('折叠内容4'),
                    Text('折叠内容5'),
                  ],
                ),
                SizedBox(height: 10),
                ExpansionMsg(
                    msgText:
                        '这是一条消息提示这是一条消息提示这是一条消息提示这是一条消息提示这是一条消息提示这是一条消息提示这是一条消息提示这是一条消息提示这是一条消息提示这是一条消息提示这是一条消息提示这是一条消息提示'),
              ]),
        ));
  }
}

四、效果演示

猜你喜欢

转载自blog.csdn.net/weixin_45731252/article/details/126710638