如何使用 Flutter Web 处理来自提供商的全局密钥?

想用flutter web构建抽屉。但是在小部件树中检测到 Duplicate GlobalKey。实例被移动到新位置。关键是:

  • [LabeledGlobalKey#c9754]

GlobalKey reparenting 是:

  • MainScreen(dependencies: [MediaQuery], state: _MainScreenState#dc897) 在小部件树中一次只能在一个小部件上指定 GlobalKey。

    
    import 'package:flutter/material.dart';
    
    class MenuController with ChangeNotifier {
          GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
    
      GlobalKey<ScaffoldState> get scaffoldKey => _scaffoldKey;
    
      void controlMenu() {
        if (!_scaffoldKey.currentState!.isDrawerOpen) {
          _scaffoldKey.currentState!.openDrawer();
        }
      }
    
      // void disposeKey() {
      //   _scaffoldKey.currentState.();
      // }
    }
    
    class _MainScreenState extends State<MainScreen> {
      @override
      void initState() {
        print('init CALLED- GAME---');
        super.initState();
      }
    
      @override
      void dispose() {
        print('DISPOSE CALLED- GAME---');
        context.read<MenuController>().scaffoldKey.currentState!.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          key: context.read<MenuController>().scaffoldKey,
          drawer: SideMenu(),
          body: SafeArea(
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                // We want this side menu only for large screen
                if (Responsive.isDesktop(context))
                  Expanded(
                    // default flex = 1
                    // and it takes 1/6 part of the screen
                    child: SideMenu(),
                  ),
                Expanded(
                  // It takes 5/6 part of the screen
                  flex: 5,
                  child: DashboardScreen(),
                ),
              ],
            ),
          ),
        );
      }}

然后想重用另一个小部件的密钥
class ProductsScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: context.read<MenuController>().scaffoldKey,
      drawer: SideMenu(),
      body: SafeArea(
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // We want this side menu only for large screen
            if (Responsive.isDesktop(context))
              Expanded(
                // default flex = 1
                // and it takes 1/6 part of the screen
                child: SideMenu(),
              ),
            Expanded(
              // It takes 5/6 part of the screen
              flex: 5,
              child: ProductsListScreen(),
            ),
          ],
        ),
      ),
    );
  }
}

并得到了错误

要在 SideMenu 小部件中导航,请使用

Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => ProductsScreen(),
),
);

stack overflow How dispose Global key from provider with flutter web?
原文答案

答案:

作者头像

您不需要手动处理 GlobalKey 。主要要求是它们不能两次插入到小部件树中。其他键( LocalKey s)不是这种情况:

// this is allowed
Row(
  children: [
    SizedBox(key: Key('hello')),
    Container(key: Key('hello')),
  ],
)

// this is not
final key = GlobalKey<ScaffoldState>();
Row(
  children: [
    Scaffold(key: key),
    Scaffold(key: key),
  ],
)

违反这一点的一个常见原因是动画。在播放一个页面和另一个页面之间的动画时,两个页面都在小部件树中,如果它们具有相同的 GlobalKey ,则会引发错误。

调用 globalKey.currentState!.dispose() 实际上会处理关联小部件的 State 。您不应该自己调用它。

相反,为第二个子树提供一个新的 GlobalKey 或在导航到新页面之前删除旧的。