Flutter - 如何确保子页面继承预期的主题数据?

问题描述 投票:0回答:1

我的应用程序的不同部分需要不同的主题。我的假设是 flutter 会应用它找到的 widget 树中最近的 ThemeData 并应用该主题。但这在导航到新页面时似乎不起作用。

例如,在下面的代码中,我希望 SecondRoute 和 ThirdRoute 都使用 FirstRoute 中提供的 ThemeData。但实际上只有SecondRoute使用了指定的ThemeData,ThirdRoute使用了MyApp中指定的ThemeData。

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(

        /// Default ThemeData specified here:
        theme: ThemeData.light().copyWith(
          scaffoldBackgroundColor: Colors.lightGreen,
          primaryColor: Colors.indigo,
        ),
        home: const FirstRoute());
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("FirstRoute"),
      ),
      body: Center(
        child: TextButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                  builder: (context) =>

                      /// New ThemeData specified here:
                      Theme(
                          data: ThemeData.light().copyWith(
                            scaffoldBackgroundColor: Colors.blue,
                            primaryColor: Colors.yellow,
                          ),
                          child: const SecondRoute())),
            );
          },
          child: Text(
            "Go To SecondRoute",
            style: TextStyle(color: Theme.of(context).primaryColor),
          ),
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("SecondRoute"),
      ),
      body: Center(
        child: TextButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => const ThirdRoute()),
            );
          },
          child: Text(
            "Go To ThirdRoute",
            style: TextStyle(color: Theme.of(context).primaryColor),
          ),
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          "ThirdRoute",
        ),
      ),
      body: Center(
        child: Text(
          "Lorem Ipsum",
          style: TextStyle(color: Theme.of(context).primaryColor),
        ),
      ),
    );
  }
}

我想要的结果是所有子页面都继承新的主题数据。

对此最好的解决方案是什么?我是否应该使用第二个 MaterialApp 而不是主题小部件,以便其所有子项都继承其主题?或者这样做会产生其他问题吗?我是否应该在每个子页面中包含一个主题小部件并为每个子页面定义主题数据?还有别的吗?

flutter themes
1个回答
0
投票

为了确保子页面继承 Flutter 应用程序中预期的

ThemeData
,您无需定义新的
MaterialApp
或手动在每个子页面中包含
Theme
小部件即可实现它。

出现此问题的原因是,当您导航到新路线(如

ThirdRoute
)时,它不再属于
Theme
中提供的
FirstRoute
Theme
小部件仅影响其在小部件树中的后代,但是当您使用
Navigator.push
导航离开时,您实际上会移出应用了
Theme
的当前小部件树。

可能的解决方案:

  1. 将导航包裹在

    Theme
    中: 为了确保
    ThirdRoute
    也继承
    SecondRoute
    提供的主题,您可以将导航包装到
    ThirdRoute
    小部件中的
    Theme
    ,如下所示:

    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => Theme(
          data: Theme.of(context), // Pass the current theme forward
          child: const ThirdRoute(),
        ),
      ),
    );
    

    这会将当前主题(从

    SecondRoute
    )传递到
    ThirdRoute

  2. 使用继承的小部件或状态管理解决方案: 如果您的应用程序有很多页面,并且您想要动态更改主题或始终应用不同的主题,那么使用

    Provider
    Riverpod
    Bloc
    等状态管理解决方案将会很有帮助。您可以使
    ThemeData
    在您的应用程序中全局可访问,并根据需要进行更新。

  3. 嵌套

    MaterialApp
    虽然用新的
    MaterialApp
    包装子页面可以通过应用单独的
    ThemeData
    来解决问题,但这不是推荐的方法。应用程序中存在多个
    MaterialApp
    可能会导致导航、主题和其他应用程序范围的行为(例如本地化和路由)出现问题。相反,请考虑上面的解决方案。

针对您的案例推荐的解决方案:

使用第一种方法(将导航包装在

Theme
小部件中)将是确保所有子页面继承所需主题而不增加不必要的复杂性的最干净、最简单的方法。

以下是将

SecondRoute
的导航修改为
ThirdRoute
的方法:

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("SecondRoute"),
      ),
      body: Center(
        child: TextButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => Theme(
                  data: Theme.of(context),  // Pass the current theme forward
                  child: const ThirdRoute(),
                ),
              ),
            );
          },
          child: Text(
            "Go To ThirdRoute",
            style: TextStyle(color: Theme.of(context).primaryColor),
          ),
        ),
      ),
    );
  }
}

这将确保

ThirdRoute
也继承
ThemeData
中设置的
SecondRoute

© www.soinside.com 2019 - 2024. All rights reserved.