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

Flutter go_router ShellRoute builder键盘弹出时多次调用问题求助

解决GoRouter ShellRoute Builder在键盘弹出时多次调用的问题

问题场景

使用GoRouter的ShellRoute时,当TextFormField获取焦点、键盘弹出,ShellRoute的builder函数会被多次调用,需要让该函数内的逻辑仅执行一次。

原因分析

键盘弹出会触发屏幕布局尺寸变化(MediaQuery更新),导致Flutter Widget树重建,ShellRoute的builder作为布局的一部分会被重新调用。如果builder中包含副作用逻辑(如打印、网络请求),就会重复执行。

解决方案

将ShellRoute的builder返回内容封装为独立的StatefulWidget,把需要仅执行一次的逻辑放到组件的initState方法中,并通过AutomaticKeepAliveClientMixin保持组件状态,避免不必要的重建执行。

修改后的代码示例

仅需修改main.dart内容,其他文件保持不变:

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

import 'debug_focus_page.dart';
import 'init_page.dart';

final _rootNavigatorKey = GlobalKey<NavigatorState>();
final _debugNavigatorKey = GlobalKey<NavigatorState>();

final _router = GoRouter(debugLogDiagnostics: true, initialLocation: "/init", navigatorKey: _rootNavigatorKey, routes: [
  GoRoute(path: '/init', builder: (context, state) => const InitPage()),
  ShellRoute(
      navigatorKey: _debugNavigatorKey,
      builder: (context, state, child) => ShellContainer(child: child),
      routes: [
        GoRoute(
          path: '/debugFocus',
          builder: (context, state) => const DebugFocusPage(),
        )
      ])
]);

void main() {
  runApp(const HomePage());
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Debug Autofocus',
      home: MaterialApp.router(
          routerConfig: _router,
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          )
          ),
    );
  }
}

// 自定义Shell容器,实现状态保持
class ShellContainer extends StatefulWidget {
  final Widget child;
  const ShellContainer({super.key, required this.child});

  @override
  State<ShellContainer> createState() => _ShellContainerState();
}

class _ShellContainerState extends State<ShellContainer> with AutomaticKeepAliveClientMixin {
  @override
  void initState() {
    super.initState();
    // 仅执行一次的逻辑放在这里
    print('ssssss');
  }

  @override
  Widget build(BuildContext context) {
    super.build(context); // 必须调用以启用状态保持
    return Scaffold(
      appBar: AppBar(),
      body: widget.child,
    );
  }

  @override
  bool get wantKeepAlive => true; // 开启状态保持
}

关键说明

  1. 将原ShellRoute builder中的UI和副作用逻辑拆分到ShellContainer组件,仅执行一次的代码(如打印)移至initState,该方法仅在组件初始化时执行一次。
  2. 通过AutomaticKeepAliveClientMixin和wantKeepAlive: true,确保组件在键盘弹出等布局变化时不会被重建,避免重复执行初始化逻辑。

内容的提问来源于stack exchange,提问作者billcyz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 08:02:48