每当键盘打开或关闭时,我的Flutter应用都会重新构建。

huangapple go评论72阅读模式
英文:

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&lt;ShowDescription&gt; createState() =&gt; _ShowDescriptionState();
}
class _ShowDescriptionState extends State&lt;ShowDescription&gt; {
List&lt;dynamic&gt; 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 &lt;TextEditingController&gt; textControllers = [];
@override
void initState(){
super.initState();
WidgetsBinding.instance?.addPostFrameCallback((_) {
FillInTheBlankWidgetState.callBackDescription = (){
textControllerListForFB.clear();
print(&quot;description callback running&quot;);
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(&quot;Rebuilding Show Description-----&quot;);
if (widget.descriptionAsString != &quot;&quot;) {
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&lt;Widget&gt;((item) {
switch (item[&#39;type&#39;]) {
case &#39;text&#39;:
if(widget.isItForFB){
String src = item[&#39;data&#39;][&#39;src&#39;];
List&lt;String&gt; parts = src.split(&#39;##&#39;);
List&lt;InlineSpan&gt; children = [];
for (int i = 0; i &lt; 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():&quot;&quot;:&quot;&quot;);
// TextEditingController controller = TextEditingController(text: (widget.isForResult)? selectedAnswerOfFB[tempIndex].toString():&quot;&quot;);
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[&#39;data&#39;][&#39;src&#39;],
),
// Text(
//   item[&#39;data&#39;][&#39;src&#39;]
//       .replaceAll(RegExp(&#39;&lt;p&gt;|&lt;\/p&gt;&#39;), &#39;\n&#39;).replaceAll(RegExp(&#39;amp;&#39;), &#39;&#39;),
// ),
);
}
case &#39;image&#39;:
return Image.network(
item[&#39;data&#39;][&#39;rootUrl&#39;] + &#39;/&#39; + item[&#39;data&#39;][&#39;src&#39;],
);
case &#39;embed&#39;:
String htmlString = item[&quot;data&quot;][&quot;src&quot;].toString();
var regExp = RegExp(&#39;src=&quot;([^&quot;]+)&quot;&#39;);
var match = regExp.firstMatch(htmlString);
if (match != null) {
var srcUrl = match.group(1);
return customWebViewWIdget(context, srcUrl);
} else {
print(&#39;No match found.&#39;);
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 == &quot;url&quot;){
print(&quot;if loop running&quot;);
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

huangapple
  • 本文由 发表于 2023年6月26日 19:07:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76556110.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定