英文:
Whenever keyboard is opend or closed, my flutter app gets rebuild
问题
每当我关闭键盘时,我在TextField
中输入的文本会被重置/清空。在尝试找出导致这种行为的问题时,我发现打开和关闭TextField
会导致应用程序重建。
以下是我遇到问题的代码部分:
part of quizui;
class ShowDescription extends StatefulWidget {
const ShowDescription({Key? key,required this.descriptionAsString,required this.isItForFB,required this.isForResult}) : super(key: key);
final bool isForResult;
final bool isItForFB;
final String descriptionAsString;
@override
State<ShowDescription> createState() => _ShowDescriptionState();
}
class _ShowDescriptionState extends State<ShowDescription> {
List<dynamic> descriptionData = [];
int tempIndex = 0;
late InAppWebViewController webView;
InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
useShouldOverrideUrlLoading: true,
mediaPlaybackRequiresUserGesture: false,
),
android: AndroidInAppWebViewOptions(
useHybridComposition: true,
),
ios: IOSInAppWebViewOptions(
allowsInlineMediaPlayback: true,
),
);
List <TextEditingController> textControllers = [];
@override
void initState(){
super.initState();
WidgetsBinding.instance?.addPostFrameCallback((_) {
FillInTheBlankWidgetState.callBackDescription = (){
textControllerListForFB.clear();
print("description callback running");
for(TextEditingController i in textControllers){
textControllerListForFB.add(i.text);
}
};
});
}
@override
void dispose() {
for (var controller in textControllers) {
controller.dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
print("Rebuilding Show Description-----");
if (widget.descriptionAsString != "") {
descriptionData = jsonDecode(widget.descriptionAsString);
}
// textControllers.clear();
tempIndex = 0;
return Container(
padding: EdgeInsets.symmetric(vertical: 5,horizontal: 8),
alignment: Alignment.centerLeft,
width: MediaQuery.of(context).size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: descriptionData.map<Widget>((item) {
switch (item['type']) {
case 'text':
if(widget.isItForFB){
String src = item['data']['src'];
List<String> parts = src.split('##');
List<InlineSpan> children = [];
for (int i = 0; i < parts.length; i++) {
if (i % 2 == 0) {
// Text outside the ##
children.add(
TextSpan(text: parseFragment(parts[i]).text)
);
} else {
// TextField inside the ##
TextEditingController controller = TextEditingController(text: (widget.isForResult)?(selectedAnswerOfFB!=[])?selectedAnswerOfFB[tempIndex].toString():"":"");
tempIndex++;
textControllers.add(controller);
children.add(
WidgetSpan(
child: SizedBox(
width: 110,
height: 40,
child: TextField(
controller: controller,
enabled: (!widget.isForResult),
decoration: InputDecoration(
// border: OutlineInputBorder(),
),style: TextStyle(fontSize: 14),
),
),
),
));
}
}
return Padding(
padding: const EdgeInsets.all(0.0),
child: RichText(
text: TextSpan(children: children,style: TextStyle(color: Colors.black)),
),
);
}
else{
return Padding(
padding: const EdgeInsets.all(5.0),
child:
Html(
data: item['data']['src'],
),
);
}
case 'image':
return Image.network(
item['data']['rootUrl'] + '/' + item['data']['src'],
);
case 'embed':
String htmlString = item["data"]["src"].toString();
var regExp = RegExp('src="([^"]+)"');
var match = regExp.firstMatch(htmlString);
if (match != null) {
var srcUrl = match.group(1);
return customWebViewWIdget(context, srcUrl);
} else {
print('No match found.');
return Container();
}
default:
return Container();
}
}).toList(),
),
);
}
Container customWebViewWIdget(BuildContext context, URL) {
return Container(
padding: EdgeInsets.all(10),
height: MediaQuery.of(context).size.width,
width: MediaQuery.of(context).size.width,
child: InAppWebView(
initialOptions: options,
shouldOverrideUrlLoading: (controller, navigationAction) async {
if (navigationAction.request.url == "url"){
print("if loop running");
return NavigationActionPolicy.CANCEL;
}
return NavigationActionPolicy.ALLOW;
},
androidOnPermissionRequest: (controller, origin, resources) async {
return PermissionRequestResponse(
resources: resources,
action: PermissionRequestResponseAction.GRANT,
);
},
initialUrlRequest: URLRequest(url: Uri.parse(URL)),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
),
);
}
}
在这段代码中,TextField
部分是重要的。如果isItForFB
为真且isForResult
为假,那么这部分非常重要。
英文:
Whenever I close the keyboard the text I write in TextField
gets reset / empty.
While trying to figure out the issue causing this behavior I came to understand that opening and closing of TextField
causes the app to rebuild.
Here is the code where I am facing the issue :
part of quizui;
class ShowDescription extends StatefulWidget {
const ShowDescription({Key? key,required this.descriptionAsString,required this.isItForFB,required this.isForResult}) : super(key: key);
final bool isForResult;
final bool isItForFB;
final String descriptionAsString;
@override
State<ShowDescription> createState() => _ShowDescriptionState();
}
class _ShowDescriptionState extends State<ShowDescription> {
List<dynamic> descriptionData = [];
int tempIndex = 0;
late InAppWebViewController webView;
InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
useShouldOverrideUrlLoading: true,
mediaPlaybackRequiresUserGesture: false,
),
android: AndroidInAppWebViewOptions(
useHybridComposition: true,
),
ios: IOSInAppWebViewOptions(
allowsInlineMediaPlayback: true,
),
);
List <TextEditingController> textControllers = [];
@override
void initState(){
super.initState();
WidgetsBinding.instance?.addPostFrameCallback((_) {
FillInTheBlankWidgetState.callBackDescription = (){
textControllerListForFB.clear();
print("description callback running");
for(TextEditingController i in textControllers){
textControllerListForFB.add(i.text);
}
};
});
}
@override
void dispose() {
for (var controller in textControllers) {
controller.dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
print("Rebuilding Show Description-----");
if (widget.descriptionAsString != "") {
descriptionData = jsonDecode(widget.descriptionAsString);
}
// textControllers.clear();
tempIndex = 0;
return Container(
padding: EdgeInsets.symmetric(vertical: 5,horizontal: 8),
alignment: Alignment.centerLeft,
// color: Colors.grey.shade200,
width: MediaQuery.of(context).size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: descriptionData.map<Widget>((item) {
switch (item['type']) {
case 'text':
if(widget.isItForFB){
String src = item['data']['src'];
List<String> parts = src.split('##');
List<InlineSpan> children = [];
for (int i = 0; i < parts.length; i++) {
if (i % 2 == 0) {
// Text outside the ##
children.add(
TextSpan(text: parseFragment(parts[i]).text)
);
} else {
// TextField inside the ##
TextEditingController controller = TextEditingController(text: (widget.isForResult)?(selectedAnswerOfFB!=[])?selectedAnswerOfFB[tempIndex].toString():"":"");
// TextEditingController controller = TextEditingController(text: (widget.isForResult)? selectedAnswerOfFB[tempIndex].toString():"");
tempIndex++;
textControllers.add(controller);
children.add(
WidgetSpan(
child: SizedBox(
width: 110,
height: 40,
child: TextField(
controller: controller,
enabled: (!widget.isForResult),
decoration: InputDecoration(
// border: OutlineInputBorder(),
),style: TextStyle(fontSize: 14),
),
),
));
}
}
return Padding(
padding: const EdgeInsets.all(0.0),
child: RichText(
text: TextSpan(children: children,style: TextStyle(color: Colors.black)),
),
);
}
else{
return Padding(
padding: const EdgeInsets.all(5.0),
child:
Html(
data: item['data']['src'],
),
// Text(
// item['data']['src']
// .replaceAll(RegExp('<p>|<\/p>'), '\n').replaceAll(RegExp('amp;'), ''),
// ),
);
}
case 'image':
return Image.network(
item['data']['rootUrl'] + '/' + item['data']['src'],
);
case 'embed':
String htmlString = item["data"]["src"].toString();
var regExp = RegExp('src="([^"]+)"');
var match = regExp.firstMatch(htmlString);
if (match != null) {
var srcUrl = match.group(1);
return customWebViewWIdget(context, srcUrl);
} else {
print('No match found.');
return Container();
}
default:
return Container();
}
}).toList(),
),
);
}
Container customWebViewWIdget(BuildContext context, URL) {
return Container(
padding: EdgeInsets.all(10),
height: MediaQuery.of(context).size.width,
width: MediaQuery.of(context).size.width,
child: InAppWebView(
initialOptions: options,
shouldOverrideUrlLoading: (controller, navigationAction) async {
if (navigationAction.request.url == "url"){
print("if loop running");
return NavigationActionPolicy.CANCEL;
}
return NavigationActionPolicy.ALLOW;
},
androidOnPermissionRequest: (controller, origin, resources) async {
return PermissionRequestResponse(
resources: resources,
action: PermissionRequestResponseAction.GRANT,
);
},
initialUrlRequest: URLRequest(url: Uri.parse(URL)),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
),
);
}
}
here isItForFB is true and isFor result is false, the only part important is where textField is related.
I am really new to flutter and have faced this issue once earlier also, i think i should use globalKey , but does not know exectly how and where.
答案1
得分: 1
是的,这是Flutter应用中常见的问题。当键盘打开或关闭时,MediaQuery.of(context)的值会发生变化,这会触发小部件树的重建。这可能会导致性能下降,特别是如果您的应用程序有很多小部件。
有几种方法可以防止在键盘打开或关闭时重建小部件树。一种方法是使用MediaQueryData.viewInsets属性来获取键盘的插入值,然后使用这些信息来相应地更新小部件的布局。另一种方法是使用InheritedWidget模式来与应用程序中的所有小部件共享键盘插入值。
参见相关问题:问题它会帮助您
英文:
Yes, this is a common issue in Flutter apps. When the keyboard is opened or closed, the MediaQuery.of(context) value changes, which triggers a rebuild of the widget tree. This can be a performance drain, especially if your app has a lot of widgets.
There are a few ways to prevent the widget tree from being rebuilt when the keyboard is opened or closed. One way is to use the MediaQueryData.viewInsets property to get the insets of the keyboard, and then use this information to update the layout of your widgets accordingly. Another way is to use the InheritedWidget pattern to share the keyboard insets with all of the widgets in your app.
see this related question : problem it will help you
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论