Flutter:KeepAlive使用错误,Drawer无法保持存活不重建
解决Drawer开关时保持存活的Incorrect use of ParentDataWidget错误
问题场景
想要实现Drawer在打开/关闭时保持状态不重建,编写了如下代码,但运行时抛出Incorrect use of ParentDataWidget错误:
原代码
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), drawer: const AutomaticKeepAlive( child: Drawer( child: MyWidget(), ), ), ); } } class MyWidget extends StatefulWidget { const MyWidget({super.key}); @override State<MyWidget> createState() => _MyWidgetState(); } class _MyWidgetState extends State<MyWidget> with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; @override Widget build(BuildContext context) { super.build(context); return const Column( children: [ Text('Input Name:'), SizedBox( width: 50, height: 30, child: TextField(), ), ], ); } }
错误信息
════════ Exception caught by widgets library ═══════════════════════════════════ The following assertion was thrown while applying parent data.: Incorrect use of ParentDataWidget. The ParentDataWidget KeepAlive(keepAlive: false) wants to apply ParentData of type KeepAliveParentDataMixin to a RenderObject, which has been set up to accept ParentData of incompatible type ParentData. Usually, this means that the KeepAlive widget has the wrong ancestor RenderObjectWidget. Typically, KeepAlive widgets are placed directly inside SliverWithKeepAliveWidget or TwoDimensionalViewport widgets. The offending KeepAlive is currently placed inside a Semantics widget. The ownership chain for the RenderObject that received the incompatible parent data was: Semantics ← Drawer ← NotificationListener<KeepAliveNotification> ← KeepAlive ← AutomaticKeepAlive ← _FocusInheritedScope ← Semantics ← FocusScope-[GlobalKey#b1269] ← RepaintBoundary ← Align ← ⋯
错误原因
AutomaticKeepAlive仅能配合支持存活状态的组件(比如ListView、PageView这类继承自SliverWithKeepAliveWidget的组件)使用,它需要父组件能处理KeepAliveParentDataMixin类型的父数据。直接把AutomaticKeepAlive包裹在Drawer外层时,Drawer的父布局是Scaffold的内部结构,无法兼容这种父数据,因此触发报错。
解决方案
将AutomaticKeepAlive移到Drawer的子组件层级,包裹需要保持状态的MyWidget,而非Drawer本身。修改后的代码如下:
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), // 移除Drawer外层的AutomaticKeepAlive drawer: const Drawer( // 将AutomaticKeepAlive放在需要保活的MyWidget外层 child: AutomaticKeepAlive( child: MyWidget(), ), ), ); } } class MyWidget extends StatefulWidget { const MyWidget({super.key}); @override State<MyWidget> createState() => _MyWidgetState(); } class _MyWidgetState extends State<MyWidget> with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; @override Widget build(BuildContext context) { super.build(context); return const Column( children: [ Text('Input Name:'), SizedBox( width: 50, height: 30, child: TextField(), ), ], ); } }
修改后,Drawer开关时MyWidget的状态会被保留,不会重新创建,同时报错问题也会解决。
内容的提问来源于stack exchange,提问作者eastwater
相关产品推荐
相关产品推荐

