You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 10:45:53