Flutter跳转导航路由时触发Incorrect use of ParentDataWidget错误
解决Flutter路由跳转时的"Incorrect use of ParentDataWidget"错误
问题场景
我在Flutter中实现了一个按钮,点击后通过以下代码跳转到使用ReadDb组件的新路由,该组件和当前页面复用构建逻辑:
onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => ReadDb(value)), ); },
但触发构建时抛出了"Incorrect use of ParentDataWidget"错误,新路由无法正常显示。
错误日志
Exception caught by widgets library ═══════════════════════════════════ The following assertion was thrown while applying parent data.: Incorrect use of ParentDataWidget. The ParentDataWidget Expanded(flex: 1) wants to apply ParentData of type FlexParentData to a RenderObject, which has been set up to accept ParentData of incompatible type ParentData. Usually, this means that the Expanded widget has the wrong ancestor RenderObjectWidget. Typically, Expanded widgets are placed directly inside Flex widgets. The offending Expanded is currently placed inside a Semantics widget. The ownership chain for the RenderObject that received the incompatible parent data was: _ScrollSemantics-[GlobalKey#b3e7b] ← NotificationListener<ScrollMetricsNotification> ← Scrollable ← PrimaryScrollController ← SingleChildScrollView ← Expanded ← FutureBuilder<Map<String, dynamic>> ← ReadDb ← Semantics ← Builder ← ⋯ When the exception was thrown, this was the stack: #0 RenderObjectElement._updateParentData.<anonymous closure> (package:flutter/src/widgets/framework.dart:6512:11) framework.dart:6512 #1 RenderObjectElement._updateParentData (package:flutter/src/widgets/framework.dart:6529:6) framework.dart:6529 #2 RenderObjectElement.attachRenderObject (package:flutter/src/widgets/framework.dart:6572:7) framework.dart:6572 #3 RenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6436:5) framework.dart:6436 #4 SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6735:11) framework.dart:6735 Normal element mounting (33 frames) #37 Element.inflateWidget (package:flutter/src/widgets/framework.dart:4326:16) framework.dart:4326 #38 Element.updateChild (package:flutter/src/widgets/framework.dart:3831:20) framework.dart:3831 #39 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16) framework.dart:5496 #40 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5634:11) framework.dart:5634 #41 Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7) framework.dart:5187 #42 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2895:19) framework.dart:2895 #43 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:984:21) binding.dart:984 #44 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:457:5) binding.dart:457 #45 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1325:15) binding.dart:1325 #46 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1255:9) binding.dart:1255 #47 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1113:5) binding.dart:1113 #48 _invoke (dart:ui/hooks.dart:312:13) hooks.dart:312 #49 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:383:5) platform_dispatcher.dart:383 #50 _drawFrame (dart:ui/hooks.dart:283:31) hooks.dart:283
相关代码
ReadDb组件代码
首次加载该组件正常,但跳转后的路由报错:
class ReadDb extends StatelessWidget { final String documentId; const ReadDb(this.documentId, {super.key}); @override Widget build(BuildContext context) { return FutureBuilder<Map<String, dynamic>>( future: fetchDocument(documentId), builder: (BuildContext context, AsyncSnapshot<Map<String, dynamic>> snapshot) { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasError) { print('Error: ${snapshot.error}'); return Text('Error: ${snapshot.error}'); } else { var data = snapshot.data; List<Widget> list = displayBasedOnType(context, documentId, data!); return Expanded( child: SingleChildScrollView( child: SafeArea( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: list, ), ), )); } } else { return const Text("loading"); } }, ); } }
displayBasedOnType函数代码
该函数返回Text和TextButton组件:
List<Widget> displayBasedOnType( BuildContext context, String documentId, Map<String, dynamic> data) { var tipo = data['tipo']; List<Widget> widgets = []; widgets.add( // Add title Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Center( child: Text( documentId, // Add this line style: const TextStyle( fontSize: 24, fontWeight: FontWeight.bold), // And this line ), ), ), ); switch (tipo) { case "Persona": { // Data e materie DateTime nascita = (data['nascita']).toDate(); String convertedNascita = DateFormat('yyyy').format(nascita); DateTime morte = (data['morte']).toDate(); String convertedMorte = DateFormat('yyyy').format(morte); widgets.add( Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.all(8.0), child: Text( "($convertedNascita - $convertedMorte)", ), ), Padding( padding: const EdgeInsets.all(8.0), child: displayMaterie(data), ) ], ), ), ); // Descrizione widgets.add( Padding( padding: const EdgeInsets.all(16), child: displayDescription(data), ), ); } widgets.add(FutureBuilder<List<Widget>>( future: displayConnections(context, data), builder: (context, snapshot) { if (snapshot.hasData) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: snapshot.data!, ); } else if (snapshot.hasError) { return Text('${snapshot.error}'); // this line prints the error } return const CircularProgressIndicator(); })); } return widgets; }
displayConnections函数代码
Future<List<Widget>> displayConnections( BuildContext context, Map<String, dynamic> data) async { List<Widget> widgets = []; for (var entry in data.entries) { if (!toExcludeFromList.contains(entry.key) && (entry.value is List)) { widgets.add( Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 12), child: Text( entry.key.capitalize(), style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), )), ); for (String value in entry.value) { bool valueExists = await documentExists(value); if (valueExists) { widgets.add((Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.fromLTRB(24, 8, 0, 8), child: TextButton( style: TextButton.styleFrom( textStyle: const TextStyle(color: Colors.blue), padding: EdgeInsets.zero, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0), ), tapTargetSize: MaterialTapTargetSize.shrinkWrap, visualDensity: VisualDensity.compact, minimumSize: const Size(0, 0), ), onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => ReadDb(value)), ); }, child: Text(value), )) ], ))); } else { widgets.add((Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.fromLTRB(24, 8, 0, 8), child: Text(value)) ], ))); } } } } return widgets; }
问题原因及解决方法
原因分析
Expanded组件的作用是在Flex(比如Column、Row)容器中分配剩余空间,它必须直接作为Flex组件的子节点使用。但跳转后的路由中,ReadDb作为页面根组件,返回的Expanded父级是路由默认添加的Semantics组件,不是Flex容器,因此触发错误。首次加载正常是因为当时ReadDb是嵌套在某个Flex组件内部的。
解决方法
把Expanded替换为Scaffold作为页面根组件,SingleChildScrollView直接放在Scaffold的body中,这样既解决了Expanded的父级问题,又能通过SingleChildScrollView处理内容溢出的情况。修改后的ReadDb组件代码如下:
class ReadDb extends StatelessWidget { final String documentId; const ReadDb(this.documentId, {super.key}); @override Widget build(BuildContext context) { return FutureBuilder<Map<String, dynamic>>( future: fetchDocument(documentId), builder: (BuildContext context, AsyncSnapshot<Map<String, dynamic>> snapshot) { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasError) { print('Error: ${snapshot.error}'); return Scaffold( body: Center(child: Text('Error: ${snapshot.error}')), ); } else { var data = snapshot.data; List<Widget> list = displayBasedOnType(context, documentId, data!); return Scaffold( body: SingleChildScrollView( child: SafeArea( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: list, ), ), ), ); } } else { return const Scaffold( body: Center(child: Text("loading")), ); } }, ); } }
如果必须保留Expanded,可以将其放在占满屏幕的Column中,示例如下:
return Scaffold( body: Column( children: [ Expanded( child: SingleChildScrollView( child: SafeArea( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: list, ), ), ), ), ], ), );
内容的提问来源于stack exchange,提问作者rummy
相关产品推荐
相关产品推荐

