flutter组件-IntrinsicWidth可使宽度适应最小值
主要是记录这种输入框的宽度自适应剩余空间
import 'dart:async';
import 'package:crm/widgets/iconfont.dart';
import 'package:flutter/material.dart';
import 'package:flutter_snsoft/flutter_snsoft.dart';
import 'package:flutter_snsoft/widgets/button/button.dart';
import 'button/button.dart';
const Duration _kExpand = Duration(milliseconds: 0);
class Inputlabel extends StatefulWidget {
/// 标签文字宽度
final double labelWidth;
/// 标签文字
final String label;
/// 显示密送
final ValueNotifier<bool>? showCC;
/// 点击密送回调
final VoidCallback? onCCTap;
/// 收件人列表
final ValueNotifier<List<Map<String, dynamic>>> addresseeList;
/// 输入框失去焦点的回调
final VoidCallback? onBlur;
/// 输入框获得焦点
final VoidCallback? onFocus;
/// 添加收件人|抄送人|密送人
final VoidCallback? onAddMail;
final bool? autoFocus;
final textController;
final focusNode;
///数据校验和重新返回数据
final Map<String, dynamic> Function(String)? rebuildFunc;
const Inputlabel(
{Key? key,
this.labelWidth = 60,
required this.label,
this.focusNode,
this.textController,
this.showCC,
this.onCCTap,
this.onBlur,
this.onFocus,
required this.addresseeList,
this.onAddMail,
this.autoFocus = false,
this.rebuildFunc})
: super(key: key);
@override
_InputlabelState createState() => _InputlabelState();
}
class _InputlabelState extends State<Inputlabel>
with SingleTickerProviderStateMixin {
// 动画控制器
late AnimationController _animationController;
late ValueNotifier<bool> showAddLinkman; //选择联系人
late bool _isExpanded; //是否是展开状态
@override
void initState() {
super.initState();
_animationController = AnimationController(duration: _kExpand, vsync: this);
_isExpanded = true;
showAddLinkman = ValueNotifier(false);
// widget.focusNode = FocusNode();
// widget.focusNode.addListener(_onBlur);
}
@override
void dispose() {
// widget.focusNode.removeListener(_onBlur);
widget.focusNode.dispose();
_animationController.dispose();
super.dispose();
}
void onItemDelete(int i) {
widget.addresseeList.value.removeAt(i);
widget.addresseeList.value = [...widget.addresseeList.value];
}
// 点击键盘完成按钮的回调
void onEditingComplete([bool clear = true]) {
if (widget.textController.text.trim() == "") {
return;
}
Map<String, dynamic>? res = checkText(widget.textController.text);
if (res == null) {
return;
}
String name = getValue(res, "name", widget.textController.text);
String addr = getValue(res, "addr", widget.textController.text);
widget.addresseeList.value = [
...widget.addresseeList.value,
{
"name": name,
"addr": addr,
}
];
if (clear) {
Timer(Duration(milliseconds: 10), () {
widget.textController.clear();
});
}
}
Map<String, dynamic>? checkText(String value) {
Map<String, dynamic> res = {"name": value, "addr": value};
if (widget.rebuildFunc != null) {
res = widget.rebuildFunc!(widget.textController.text);
if (getValue(res, "err", false)) {
Toast.show(getValue(res, "errmsg", "请检查输入信息"));
return null;
}
}
return res;
}
void _onBlur() {
if (widget.focusNode.hasFocus) {
_isExpanded = true;
showAddLinkman.value = true;
setState(() {
_animationController.forward();
});
if (widget.onFocus != null) {
widget.onFocus!();
}
} else {
// 失去焦点的时候先插入一次
onEditingComplete();
showAddLinkman.value = false;
// _isExpanded = false;
_animationController.reverse().then<void>((void value) {
if (!mounted) return;
setState(() {
// Rebuild without widget.children.
});
});
if (widget.onBlur != null) {
widget.onBlur!();
}
}
}
void onEdit(int i, String email) {
onEditingComplete(false);
widget.textController.text = email;
widget.textController.selection = TextSelection.fromPosition(
TextPosition(affinity: TextAffinity.downstream, offset: email.length));
widget.addresseeList.value.removeAt(i);
widget.addresseeList.value = [...widget.addresseeList.value];
}
Widget _buildChildren(context, child) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
FocusScope.of(context).requestFocus(widget.focusNode);
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.fromLTRB(16, 4, 16, 0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: widget.labelWidth,
height: 28,
child: Align(
alignment: Alignment.centerLeft,
child: Text(
widget.label,
style: TextStyle(
fontSize: 15,
color: Colors.black,
fontWeight: FontWeight.w500,
height: 1),
),
),
),
Expanded(
child: child,
),
if (widget.showCC != null)
ValueListenableBuilder(
valueListenable: widget.showCC!,
builder: (context, bool val, child) {
if (!val) {
return SizedBox(
height: 28,
child: Center(
child: TextsButton(
text: "密送",
color: Colors.black,
onTap: widget.onCCTap,
lineHeight: 1,
),
),
);
} else {
return SizedBox(
height: 28,
child: Center(
child: TextsButton(
text: "取消密送",
color: Colors.black,
onTap: widget.onCCTap,
lineHeight: 1,
),
),
);
}
},
),
WIconButton(
icon: CRMIcon.xieyoujian_jiashoujianren,
iconSize: 20,
iconColor: Colors.black,
padding: const EdgeInsets.fromLTRB(8, 3, 0, 8),
onTap: widget.onAddMail!,
)
// Transform.translate(
// offset: const Offset(0, -2),
// child: ValueListenableBuilder(
// valueListenable: showAddLinkman,
// builder: (context, bool value, child) {
// return Offstage(
// offstage: value,
// child: WIconButton(
// icon: Icons.add,
// iconSize: 20,
// iconColor: Colors.black,
// padding: const EdgeInsets.fromLTRB(8, 5, 0, 8),
// onTap: widget.onAddMail!,
// ),
// );
// },
// ),
// ),
],
),
),
const Divider(height: 12),
],
));
}
@override
Widget build(BuildContext context) {
final bool closed = !_isExpanded && _animationController.isDismissed;
final Widget result = Offstage(
child: TickerMode(
child: _Linkmans(
children: widget.addresseeList,
onItemTap: onItemDelete,
onEdit: onEdit,
autoFocus: widget.autoFocus!,
controller: widget.textController,
focusNode: widget.focusNode,
onEditingComplete: onEditingComplete,
),
enabled: !closed,
),
offstage: closed);
return AnimatedBuilder(
animation: _animationController.view,
builder: _buildChildren,
child: result,
);
}
}
class _Input extends StatelessWidget {
///输入控制器
final TextEditingController controller;
/// 焦点管理
final FocusNode? focusNode;
/// 键盘确定事件
final VoidCallback? onEditingComplete;
final bool autoFocus;
const _Input(
{Key? key,
required this.controller,
this.focusNode,
this.onEditingComplete,
this.autoFocus = false})
: super(key: key);
@override
Widget build(BuildContext context) {
return IntrinsicWidth(
child: TextField(
autofocus: autoFocus,
controller: controller,
cursorColor: Colors.black,
cursorWidth: 1,
cursorHeight: 18,
scrollPadding: EdgeInsets.zero,
focusNode: focusNode,
decoration: const InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.all(5),
isDense: true,
),
style: const TextStyle(height: 1),
onEditingComplete: onEditingComplete,
),
);
}
}
class _Linkmans extends StatefulWidget {
// 要渲染的收件人
final ValueNotifier<List<Map>> children;
/// 子组件点击事件
final ValueChanged<int>? onItemTap;
/// 子组件点击编辑
final Function(int, String)? onEdit;
final autoFocus;
final controller;
final focusNode;
final onEditingComplete;
const _Linkmans(
{Key? key,
required this.children,
this.onItemTap,
this.onEdit,
this.autoFocus,
this.controller,
this.focusNode,
this.onEditingComplete})
: super(key: key);
@override
_LinkmansState createState() => _LinkmansState();
}
class _LinkmansState extends State<_Linkmans> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
void _onEdit(int i, String email) {
if (widget.onEdit != null) {
widget.onEdit!(i, email);
}
}
List<Widget> buildChildren(List list) {
List<Widget> res = [];
for (int i = 0; i < list.length; i++) {
res.add(EmailText(
name: list[i]["name"] ?? list[i]["addr"] ?? "",
email: list[i]["addr"] ?? "",
onTap: () => onTap(i),
onEdit: () => _onEdit(i, list[i]["addr"] ?? ""),
));
}
res.add(_Input(
autoFocus: widget.autoFocus!,
controller: widget.controller,
focusNode: widget.focusNode,
onEditingComplete: widget.onEditingComplete,
));
return res;
}
void onTap(int i) {
if (widget.onItemTap != null) {
widget.onItemTap!(i);
}
}
@override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: widget.children,
builder: (context, List children, child) {
return Padding(
padding: const EdgeInsets.only(right: 2, left: 2),
child: Wrap(
spacing: 8,
runSpacing: 8,
children: buildChildren(children),
),
);
},
);
}
}
class EmailText extends StatelessWidget {
/// 对于手工录入的邮箱来说,name和email的值是相等的
///
///名称
final String? name;
/// 邮箱
final String? email;
///点击回调
final VoidCallback? onTap;
///重新编辑邮箱
final VoidCallback? onEdit;
const EmailText({Key? key, this.name, this.email, this.onTap, this.onEdit})
: super(key: key);
@override
Widget build(BuildContext context) {
// 录入的信息是不是一个有效的邮箱地址
bool _isEmail = isMailLegal(email!.trim());
late ValueNotifier<bool> isEml = ValueNotifier(_isEmail);
// 获取邮箱的背景色
Color _normalBgColor = _isEmail ? Color(0xffe7f1ff) : Color(0xffffe8ee);
String _name;
if (isMailLegal(name!.trim())) {
// 如果是手动录入的邮箱
var list = name!.trim().split("@");
_name = subString(list[0], 0, 10) + "@" + list[1];
} else {
_name = subString(name!.trim(), 0, 6);
}
return GestureDetector(
onTap: onEdit,
child: Container(
height: 28,
padding: const EdgeInsets.fromLTRB(10, 2, 0, 0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
ValueListenableBuilder(
valueListenable: isEml,
builder: (context, bool value, child) {
return value
? Container(
width: 24,
height: 24,
margin: const EdgeInsets.only(right: 6),
decoration: BoxDecoration(
color: const Color(0xff4b86fc),
borderRadius: BorderRadius.circular(12),
),
alignment: Alignment.center,
child: Text(_name.substring(0, 1)),
)
: const SizedBox();
},
),
Text(
_name,
style: TextStyle(
fontSize: 14,
color: _isEmail ? Colors.black : Color(0xffff4377),
height: 1,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
WIconButton(
icon: Icons.close,
iconSize: 16,
iconColor: Colors.black.withOpacity(0.2),
padding: EdgeInsets.only(left: 6, right: 10, top: 5, bottom: 6),
onTap: onTap!,
),
],
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
color: _normalBgColor,
),
),
);
}
}
String subString(String text, int start, int maxLen) {
if (text.length - start <= maxLen) {
return text.substring(start);
} else {
return text.substring(start, maxLen) + "...";
}
}
本人小白,各位想踏入前端的,我们可以一起学习,欢迎程序员大佬的指点