flutter

flutter 自定义收缩组件

2021-08-16  本文已影响0人  卢融霜

运行效果

QQ录屏20210817120142202181712281.gif

收缩组件类

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

/// @description 作用:收缩组件
/// @date: 2021/8/17
/// @author:卢融霜
class ShrinkWidget extends StatefulWidget {
  //标题位置
  Widget titleWidget;

  //收缩位置
  Widget shrinkWidget;

  //收缩位置默认展示视图,默认为空
  Widget shrinkVisibleWidget;

  ShrinkWidget(
      {Key key,
      @required this.titleWidget,
      @required this.shrinkWidget,
      this.shrinkVisibleWidget})
      : super(key: key);

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

CrossFadeState visible = CrossFadeState.showFirst;

class _ShrinkWidgetState extends State<ShrinkWidget>
    with SingleTickerProviderStateMixin {
  Animation _shAnimation;
  AnimationController shController;

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

  @override
  void initState() {
    super.initState();
    shController = AnimationController(
        vsync: this, duration: const Duration(milliseconds: 200));
    _shAnimation = Tween(begin: .0, end: .5).animate(shController);
  }

  @override
  Widget build(BuildContext context) {
    return Ink(
      color: Colors.white,
      child: InkWell(
          onTap: () {
            setState(() {
              if (visible == CrossFadeState.showFirst) {
                visible = CrossFadeState.showSecond;
                shController.forward();
              } else {
                visible = CrossFadeState.showFirst;
                shController.reverse();
              }
            });
          },
          child: Container(
            child: Column(
              children: [
                Row(
                  children: [
                    Expanded(child: widget.titleWidget),
                    Padding(
                        padding: EdgeInsets.only(left: 20.r),
                        child: RotationTransition(
                          turns: _shAnimation,
                          child: Icon(
                            Icons.arrow_drop_down_sharp,
                            size: 24.r,
                          ),
                        ))
                  ],
                ),
                AnimatedCrossFade(
                    firstChild:
                        widget.shrinkVisibleWidget ?? Container(height: 0),
                    duration: const Duration(milliseconds: 200),
                    crossFadeState: visible,
                    secondChild: widget.shrinkWidget)
              ],
            ),
            padding: EdgeInsets.all(10.r),
            decoration: BoxDecoration(
                border: Border(
                    bottom: BorderSide(
                        color: const Color.fromRGBO(230, 230, 230, 1),
                        width: 0.4.r))),
          )),
    );
  }
}

使用组件

import 'package:flutter/material.dart';
import 'package:lnsl_credit_flutter/base/base_routes_widget.dart';
import 'package:lnsl_credit_flutter/widget/shrink_widget.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

/// @description 作用:收缩组件测试
/// @date: 2021/8/17
/// @author:卢融霜
class ShrinkWidgetTest extends StatefulWidget {
  const ShrinkWidgetTest({Key key}) : super(key: key);

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

class _ShrinkWidgetTestState extends State<ShrinkWidgetTest> {
  @override
  Widget build(BuildContext context) {
    return BaseRoutesWidget(
      title: "收缩组件测试",
      child: ListView.builder(
          itemCount: 20,
          itemBuilder: (BuildContext context, int index) {
            return PersonnelItemWidget();
          }),
    );
  }
}

class PersonnelItemWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _PersonnelItemWidget();
  }

  const PersonnelItemWidget();
}

class _PersonnelItemWidget extends State<PersonnelItemWidget> {
  CrossFadeState visible = CrossFadeState.showFirst;

  @override
  Widget build(BuildContext context) {
    return ShrinkWidget(
        titleWidget: Row(
          children: [
            Expanded(
                child: Text(
              "卢融霜",
              style: TextStyle(
                  overflow: TextOverflow.ellipsis,
                  fontSize: 15.r,
                  fontWeight: FontWeight.bold,
                  color: const Color.fromRGBO(51, 51, 51, 1)),
            )),
            Container(
              width: 100.r,
              margin: EdgeInsets.only(left: 20.r),
              padding:
                  EdgeInsets.only(left: 5.r, right: 5.r, top: 2.r, bottom: 2.r),
              decoration: BoxDecoration(
                color: const Color.fromRGBO(228, 244, 253, 1),
                borderRadius: BorderRadius.all(Radius.circular(2.r)),
              ),
              child: Center(
                child: Text(
                  "工程师",
                  style: TextStyle(
                      overflow: TextOverflow.ellipsis,
                      fontSize: 14.r,
                      color: const Color.fromRGBO(0, 136, 245, 1)),
                ),
              ),
            )
          ],
        ),
        shrinkWidget: Container(
          margin: EdgeInsets.only(top: 10.r),
          padding: EdgeInsets.only(top: 10.r),
          decoration: BoxDecoration(
              border: Border(
                  top: BorderSide(
                      color: const Color.fromRGBO(231, 231, 231, 1),
                      width: 0.4.r))),
          child: Column(
            children: [
              Row(
                children: [
                  Text(
                    "职    称",
                    style: TextStyle(
                        fontSize: 14.r,
                        color: const Color.fromRGBO(170, 170, 170, 1)),
                  ),
                  Padding(
                    padding: EdgeInsets.only(left: 20.r),
                    child: Text(
                      "工程师",
                      style: TextStyle(
                          fontSize: 14.r,
                          fontWeight: FontWeight.bold,
                          color: const Color.fromRGBO(51, 51, 51, 1)),
                    ),
                  )
                ],
              ),
              Padding(
                padding: EdgeInsets.only(top: 10.r),
                child: Row(
                  children: [
                    Text(
                      "技    能",
                      style: TextStyle(
                          fontSize: 14.r,
                          color: const Color.fromRGBO(170, 170, 170, 1)),
                    ),
                    Padding(
                      padding: EdgeInsets.only(left: 20.r),
                      child: Text(
                        "java、vue、flutter、kotlin",
                        style: TextStyle(
                            fontSize: 14.r,
                            fontWeight: FontWeight.bold,
                            color: const Color.fromRGBO(51, 51, 51, 1)),
                      ),
                    )
                  ],
                ),
              )
            ],
          ),
        ));
  }
}

涉及到的其他组件 BaseRoutesWidget

import 'dart:ui';

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

/// @description 作用:封装导航栏
/// @date: 2021/7/14
/// @author:卢融霜
class BaseRoutesWidget extends StatefulWidget {
  //标题
  String title;
  double titleFontSize = 14.r;
  double toolBarHeight = 40.r;
  List<Widget> actions;
  Widget child;
  PreferredSizeWidget titleBottom;
  Color titleBgColor;
  bool showAppBar = true;
  bool isSearchBar = false;
  Function(String searchText) search;
  FloatingActionButton floatingActionButton;

  BaseRoutesWidget(
      {Key key,
      this.title,
      this.actions,
      this.child,
      this.titleBottom,
      this.titleBgColor,
      this.showAppBar = true,
      this.isSearchBar = false,
      this.search,
      this.floatingActionButton})
      : super(key: key);

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

class _BaseRoutesWidgetState extends State<BaseRoutesWidget> {
  TextEditingController controller;

  @override
  void initState() {
    if (widget.isSearchBar) {
      controller = TextEditingController();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: widget.floatingActionButton,
      resizeToAvoidBottomInset: false,
      appBar: widget.showAppBar
          ? (widget.isSearchBar
              ? AppBar(
                  leadingWidth: 30.r,
                  toolbarHeight: widget.toolBarHeight,
                  centerTitle: true,
                  actions: widget.actions,
                  bottom: widget.titleBottom,
                  title: Row(
                    children: [
                      Expanded(
                          child: Container(
                        margin: EdgeInsets.only(right: 10.r),
                        decoration: BoxDecoration(
                            borderRadius:
                                BorderRadius.all(Radius.circular(20.r)),
                            color: const Color.fromRGBO(242, 242, 242, 1)),
                        padding: EdgeInsets.only(
                            left: 10.r, right: 10.r, top: 4.r, bottom: 4.r),
                        child: TextField(
                          textInputAction: TextInputAction.search,
                          controller: controller,
                          style: TextStyle(fontSize: 13.r),
                          onEditingComplete: () {
                            widget.search(controller.text);
                          },
                          decoration: InputDecoration(
                            hintText: "请输入关键字",
                            hintStyle: TextStyle(fontSize: 13.r),
                            border: InputBorder.none,
                            isCollapsed: true,
                            icon: Icon(
                              Icons.search_outlined,
                              size: 18.r,
                            ),
                          ),
                        ),
                      )),
                      InkWell(
                        splashColor: Colors.transparent,
                        child: Text(
                          "搜索",
                          style: TextStyle(fontSize: 14.r),
                        ),
                        onTap: () {
                          widget.search(controller.text);
                        },
                      )
                    ],
                  ),
                )
              : AppBar(
                  toolbarHeight: widget.toolBarHeight,
                  centerTitle: true,
                  actions: widget.actions,
                  bottom: widget.titleBottom,
                  title: Text(widget.title,
                      style: TextStyle(fontSize: widget.titleFontSize)),
                ))
          : null,
      body: widget.child,
    );
  }
}

为了兼容列表回收机制,将 收缩状态 和 收缩监听 进行了提取

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

/// @description 作用:收缩组件
/// @date: 2021/8/17
/// @author:卢融霜
class ShrinkWidget extends StatefulWidget {
  //标题位置
  Widget titleWidget;

  //收缩位置
  Widget shrinkWidget;

  //收缩位置默认展示视图,默认为空
  Widget shrinkVisibleWidget;

  //收缩状态
  CrossFadeState visible;

  //收缩状态监听
  Function(CrossFadeState visible, AnimationController shController)
      stateListener;

  ShrinkWidget(
      {Key key,
      @required this.titleWidget,
      @required this.shrinkWidget,
      this.visible = CrossFadeState.showFirst,
      this.shrinkVisibleWidget,
      this.stateListener})
      : super(key: key);

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

class _ShrinkWidgetState extends State<ShrinkWidget>
    with SingleTickerProviderStateMixin {
  Animation _shAnimation;
  AnimationController shController;

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

  @override
  void initState() {
    super.initState();
    shController = AnimationController(
        vsync: this, duration: const Duration(milliseconds: 200));
    _shAnimation = Tween(begin: .0, end: .5).animate(shController);
  }

  @override
  Widget build(BuildContext context) {
    return Ink(
      color: Colors.white,
      child: InkWell(
          onTap: () {
            widget.stateListener(widget.visible, shController);
          },
          child: Container(
            child: Column(
              children: [
                Row(
                  children: [
                    Expanded(child: widget.titleWidget),
                    Padding(
                        padding: EdgeInsets.only(left: 20.r),
                        child: RotationTransition(
                          turns: _shAnimation,
                          child: Icon(
                            Icons.arrow_drop_down_sharp,
                            size: 24.r,
                          ),
                        ))
                  ],
                ),
                AnimatedCrossFade(
                    firstChild:
                        widget.shrinkVisibleWidget ?? Container(height: 0),
                    duration: const Duration(milliseconds: 200),
                    crossFadeState: widget.visible,
                    secondChild: widget.shrinkWidget)
              ],
            ),
            padding: EdgeInsets.all(10.r),
            decoration: BoxDecoration(
                border: Border(
                    bottom: BorderSide(
                        color: const Color.fromRGBO(230, 230, 230, 1),
                        width: 0.4.r))),
          )),
    );
  }
}

上一篇下一篇

猜你喜欢

热点阅读