Flutter Drawer记住选中项问题:条件样式不生效
解决Flutter Drawer复用与菜单项高亮不生效的问题
我来帮你分析一下问题所在,然后给出两种可行的解决方案:
问题根源
你当前的代码里有两个核心问题导致条件样式不生效:
- 执行顺序错误:你在
onTap里先调用了Navigator.pop(ctxt)关闭Drawer,之后才调用setState更新选中状态。这时候Drawer已经被移除出Widget树了,setState触发的UI更新根本不会被渲染出来。 - 状态与页面不绑定:全局的
sameDrawerOnly实例会保留之前的选中状态,当你从新页面回到首页时,Drawer还是会显示之前选中的菜单项,不符合“当前页面对应菜单项高亮”的预期。
方案一:调整执行顺序(快速修复)
只需要把setState移到Navigator.pop之前,这样在关闭Drawer前就能完成状态更新并渲染高亮样式:
// 修改DrawerOnly中的ListTile onTap逻辑 ListTile( title: Text("Item => A", style: itemClicked==1 ? TextStyle( fontWeight: FontWeight.bold, color: Colors.red.withOpacity(0.6) ) : null), onTap: () { // 先更新状态,再关闭Drawer setState(() { itemClicked=1; }); Navigator.pop(ctxt); Navigator.push(ctxt, MaterialPageRoute(builder: (ctxt) => FirstPage())); }, ),
同时,如果你希望回到首页时恢复默认选中状态,可以把MyHomePage改成StatefulWidget,在initState里重置状态:
class MyHomePage extends StatefulWidget { final String title; MyHomePage({Key key, this.title}) : super(key: key); @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override void initState() { super.initState(); // 重置Drawer的选中状态 sameDrawerOnly.setState(() { sameDrawerOnly.state.itemClicked = 0; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(widget.title)), body: Center(child: Text('My Page!')), drawer: sameDrawerOnly, ); } }
方案二:根据路由自动高亮(推荐)
这种方法不需要维护额外的状态变量,而是通过当前路由自动判断应该高亮哪个菜单项,更可靠也更符合页面与菜单的对应关系:
步骤1:配置命名路由
修改MyApp,使用命名路由管理页面跳转:
class MyApp extends StatelessWidget { final appTitle = 'Drawer Demo'; @override Widget build(BuildContext context) { return MaterialApp( title: appTitle, initialRoute: '/', // 首页路由 routes: { '/': (context) => MyHomePage(title: appTitle), '/first': (context) => FirstPage(), '/second': (context) => SecondPage(), }, ); } }
步骤2:修改DrawerOnly,根据路由判断高亮
去掉itemClicked变量,在build方法中获取当前路由名称,动态设置样式:
class DrawerOnly extends StatefulWidget { const DrawerOnly ({ Key key, }) : super(key: key); @override _DrawerOnlyState createState() => _DrawerOnlyState(); } class _DrawerOnlyState extends State<DrawerOnly > { @override Widget build(BuildContext ctxt) { // 获取当前页面的路由名称 final currentRoute = ModalRoute.of(ctxt)?.settings?.name; return Drawer( child: ListView( children: <Widget>[ DrawerHeader( child: Text("DRAWER HEADER.."), decoration: BoxDecoration(color: Colors.orange), ), ListTile( title: Text("Item => A", style: currentRoute == '/first' ? TextStyle( fontWeight: FontWeight.bold, color: Colors.red.withOpacity(0.6) ) : null), onTap: () { Navigator.pop(ctxt); Navigator.pushNamed(ctxt, '/first'); // 使用命名路由跳转 }, ), ListTile( title: Text("Item => 2", style: currentRoute == '/second' ? TextStyle( fontWeight: FontWeight.bold , color: Colors.green.withOpacity(0.6) ) : null), onTap: () { Navigator.pop(ctxt); Navigator.pushNamed(ctxt, '/second'); // 使用命名路由跳转 }, ), ] ) ); } }
步骤3:每个页面直接使用DrawerOnly实例
不需要再使用全局的sameDrawerOnly,每个页面的drawer直接创建DrawerOnly()即可:
class MyHomePage extends StatelessWidget { final String title; MyHomePage({Key key, this.title}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(title)), body: Center(child: Text('My Page!')), drawer: DrawerOnly(), ); } } class FirstPage extends StatelessWidget { @override Widget build(BuildContext ctxt) { return Scaffold( drawer: DrawerOnly(), appBar: AppBar(title: Text("First Page")), body: Text("I belongs to First Page"), ); } } class SecondPage extends StatelessWidget { @override Widget build(BuildContext ctxt) { return Scaffold( drawer: DrawerOnly(), appBar: AppBar(title: Text("Second Page")), body: Text("I belongs to Second Page"), ); } }
这种方法的优势在于:
- 不需要维护额外的状态变量,避免状态不同步的问题
- 自动适配当前页面,打开Drawer时自动高亮对应菜单项
- 不需要全局实例,代码更简洁可维护
内容的提问来源于stack exchange,提问作者user12021836
相关产品推荐
相关产品推荐

