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))),
)),
);
}
}