在此记录项目中所遇到问题及解决方案
这几天发现TextField Selection Bar 竟然随着滚动条滚出界了,Goo 了 半天发现是Flutter 系统BUG。
但没有找到具体解决方案,只能自己想办法咯。
TextField 有提供一个控制:TextSelectionControls, 但不直接提供 Selection Bar 的 Show 和 Hide 方法,而是
放在TextSelectionDelegate里。TextSelectionControls 是 abstract 类,Android 系统是MaterialTextSelectionControls,
IOS系统是CupertinoTextSelectionControls。继承它们就可以了,并在ScrollController 里控制即可。
直接看代码更直观一点(以下代码是不完整的,请找关键代码使用)
class RecordCertificateFragmentState extends State
with AutomaticKeepAliveClientMixin, WidgetsBindingObserver{
final _scrollController = ScrollController();
TextSelectionControls? _selectionControls;
MyMaterialTextSelectionControls? _myTextSelectionControls;
MyCupertinoTextSelectionControls? _myIOSTextSelectionControls;
@override
bool get wantKeepAlive => true;
@override
void initState() {
super.initState();
///这里只是简单控制,只有滚动了就隐藏
_scrollController?.addListener(() {
_myIOSTextSelectionControls?.hide();
_myTextSelectionControls?.hide();
});
if(Platform.isAndroid){
_myTextSelectionControls = MyMaterialTextSelectionControls();
_selectionControls = _myTextSelectionControls;
}else if(Platform.isIOS){
_myIOSTextSelectionControls = MyCupertinoTextSelectionControls();
_selectionControls = _myIOSTextSelectionControls;
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
super.build(context);
final padding = 40.w;
final imageWidth = MediaQuery.of(context).size.width - padding * 2;
final imageHeight = (imageWidth / 3) * 4;
return AnnotatedRegion(
value: SystemUiOverlayStyle.light,
child: Scaffold(
body: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(top: 94.w, left: padding, right: padding),
child: Text(AppLocalizations.of(context)!.recordYourCertificate,
style: TextStyle(
color: Colors.white,
fontSize: 40.sp,
fontWeight: FontWeight.w600,height: 1.2
),
),
),
SizedBox(height: 45.w,),
Expanded(
child: SingleChildScrollView(
///这里是滚动条的控制
controller: _scrollController,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 40.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _buildDetail(),
),
),
]
),
),
)
],
),
],
),
),
);
}
List _buildDetail(){
final children = [];
_addEditRow(children, AppLocalizations.of(context)!.recordYourCertificateTopic, '',
_topicController,false,0,focusNode: _topicFocusNode,selectionControls: _selectionControls);
_addEditRow(children,
AppLocalizations.of(context)!.recordYourCertificateCategory,
AppLocalizations.of(context)!.editProfilePleaseSelect,
_cateController, true,1
);
_addEditRow(children,
AppLocalizations.of(context)!.recordYourCertificateName,
'',
_organNameController, false,0,focusNode: _organNameFocusNode,
selectionControls: _selectionControls
);
_addEditRow(children,
AppLocalizations.of(context)!.recordYourCertificateIssue,
AppLocalizations.of(context)!.editProfilePleaseSelect,
_issueDateController, true, 3
);
children.add(Text(AppLocalizations.of(context)!.recordYourCertificateUse,
style: TextStyle(
color: editTitleColor,
fontSize: 12.sp,
fontWeight: FontWeight.w700
),));
children.add(SizedBox(height: 20.w,));
children.add(Wrap(
spacing: 7.w,
runSpacing: 15.w,
children: _buildTag(_useInformations),
));
children.add(SizedBox(height: 22.w,));
_addEditRow(children,
AppLocalizations.of(context)!.recordYourCertificateDescription, '',
_descriptionController,false,0,textFormFieldHeight: 105.w,alignment: Alignment.topLeft,
focusNode: _descriptionFocusNode,selectionControls: _selectionControls);
children.add(
buildButton(AppLocalizations.of(context)!.recordYourCertificateCreate,
margin: EdgeInsets.symmetric(vertical: 31.w),
height: 72.w,
fontSize: 20.sp,
onPressed: () {
//_submit();
_intervalClick(1100,_submit);
},
),
);
return children;
}
_addEditRow(List children,String title,String value,
TextEditingController? controller,bool isSelected,int type,
{double? textFormFieldHeight,Alignment? alignment,FocusNode? focusNode,
TextSelectionControls? selectionControls,}){
children.add(Text(title, style: TextStyle(
color: editTitleColor,
fontSize: 12.sp,
fontWeight: FontWeight.w700
),));
children.add(SizedBox(height: 6.w,));
children.add(
Stack(
children: [
Container(
alignment: alignment??Alignment.center,
width: double.infinity,
height: textFormFieldHeight??56.w,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(width: 1.w, color: Color.fromARGB(255, 222, 222, 224)),
),
padding: EdgeInsets.symmetric(horizontal:16.w),
child: TextFormField(
///这里就是 selectionControls
selectionControls: selectionControls,
controller: controller,
focusNode: focusNode,
enabled: !isSelected,
style: TextStyle(
fontSize: 16.sp,
color: editValueColor,
),
keyboardType: alignment!=null? TextInputType.multiline:null,
minLines: alignment!=null? 3: null,
maxLines: alignment!=null? 30: 1,
decoration: InputDecoration(
border: InputBorder.none,
),
buildCounter: (BuildContext context, { int? currentLength, int? maxLength, bool? isFocused }) => null,
),
),
if(isSelected)
Positioned(
top: 8.w,
right: 5.w,
bottom: 8.w,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: (){
_unfocusAll();
switch(type){
case 1:
_showCountiesCupertinoPicker();
break;
case 3:
_showCupertinoDatePicker();
break;
}
},
child: Image(
image: AssetImage('files/icon_chevron_down.png'),
width: 40.w,
height: 40.w,
),
),
),
),
],
)
);
children.add(SizedBox(height: 15.w,));
}
TextSelectionControls 子类:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class MyMaterialTextSelectionControls extends MaterialTextSelectionControls {
TextSelectionDelegate? myTextSelectionDelegate;
@override
Widget buildHandle(BuildContext context, TextSelectionHandleType type, double textLineHeight) {
return Container();//super.buildHandle(context,type,textLineHeight);
}
@override
Widget buildToolbar(BuildContext context, Rect globalEditableRegion, double textLineHeight, Offset position,
List endpoints, TextSelectionDelegate delegate, ClipboardStatusNotifier clipboardStatus,
Offset? lastSecondaryTapDownPosition) {
myTextSelectionDelegate = delegate;
return super.buildToolbar(context,globalEditableRegion,textLineHeight,position,endpoints,delegate,clipboardStatus,
lastSecondaryTapDownPosition);
}
void hide(){
debugPrint("myTextSelectionDelegate hideToolbar");
myTextSelectionDelegate?.hideToolbar();
}
@override
Offset getHandleAnchor(TextSelectionHandleType type, double textLineHeight) {
return super.getHandleAnchor(type,textLineHeight);
}
@override
Size getHandleSize(double textLineHeight) {
//debugPrint("MyTextSelectionControls getHandleSize $textLineHeight");
return super.getHandleSize(textLineHeight);
}
}
class MyCupertinoTextSelectionControls extends CupertinoTextSelectionControls {
TextSelectionDelegate? myTextSelectionDelegate;
@override
Widget buildHandle(BuildContext context, TextSelectionHandleType type, double textLineHeight) {
return Container();//super.buildHandle(context,type,textLineHeight);
}
@override
Widget buildToolbar(BuildContext context, Rect globalEditableRegion, double textLineHeight, Offset position,
List endpoints, TextSelectionDelegate delegate, ClipboardStatusNotifier clipboardStatus,
Offset? lastSecondaryTapDownPosition) {
myTextSelectionDelegate = delegate;
return super.buildToolbar(context,globalEditableRegion,textLineHeight,position,endpoints,delegate,clipboardStatus,
lastSecondaryTapDownPosition);
}
void hide(){
//debugPrint("myTextSelectionDelegate hideToolbar");
myTextSelectionDelegate?.hideToolbar();
}
@override
Offset getHandleAnchor(TextSelectionHandleType type, double textLineHeight) {
//debugPrint("MyTextSelectionControls getHandleAnchor $textLineHeight");
return super.getHandleAnchor(type,textLineHeight);
}
@override
Size getHandleSize(double textLineHeight) {
//debugPrint("MyTextSelectionControls getHandleSize $textLineHeight");
return super.getHandleSize(textLineHeight);
}
}