当我渲染我的子材质应用程序时,如何阻止我的更改通知程序提供程序重建我的父材质应用程序?

Mik*_*ink 3 dart flutter flutter-change-notifier

我有一个应用程序类,它返回一个MaterialApp()它的主页设置为TheSplashPage(). 如果任何首选项发生更改,此应用程序会侦听首选项通知程序。

然后TheSplashPage()我等待一些条件为真,如果是的话,我会向他们展示我的嵌套材料应用程序。

旁注:我在这里使用材料应用程序,因为它看起来更合乎逻辑,因为它具有父材料应用程序不应该具有的路由。而且一旦用户未经身份验证或断开连接,我希望整个嵌套应用程序关闭并显示另一个页面。这很好用!

但我的问题如下。ThePreferencesProvider()当主题更改时,这两个应用程序都会收听,因此它们都会收到通知并重新构建。但这是一个问题,因为每当父材质应用程序重建时,它都会返回启动页面。所以现在TheSplashPage()每当我更改TheSettingsPage().

所以我的问题是如何阻止我的应用程序TheSplashPage()在我更改设置时返回到?

Main.dart

void main() {
  runApp(App());
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    SystemChrome.setEnabledSystemUIOverlays([]);

    return MultiProvider(
      providers: [
        ChangeNotifierProvider<PreferencesProvider>(create: (_) => PreferencesProvider()),
        ChangeNotifierProvider<ConnectionProvider>(
          create: (_) => ConnectionProvider(),
        ),
        ChangeNotifierProvider<AuthenticationProvider>(create: (_) => AuthenticationProvider()),
      ],
      child: Consumer<PreferencesProvider>(builder: (context, preferences, _) {
        return MaterialApp(
          home: TheSplashPage(),
          theme: preferences.isDarkMode ? DarkTheme.themeData : LightTheme.themeData,
          debugShowCheckedModeBanner: false,
        );
      }),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

SplashPage.dart

class TheSplashPage extends StatelessWidget {
  static const int fakeDelayInSeconds = 2;

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: Future.delayed(new Duration(seconds: fakeDelayInSeconds)),
        builder: (context, delaySnapshot) {
          return Consumer<ConnectionProvider>(
              builder: (BuildContext context, ConnectionProvider connectionProvider, _) {

            if (delaySnapshot.connectionState != ConnectionState.done ||
                connectionProvider.state == ConnectionStatus.uninitialized) return _buildTheSplashPage(context);

            if (connectionProvider.state == ConnectionStatus.none) return TheDisconnectedPage();

            return Consumer<AuthenticationProvider>(
                builder: (BuildContext context, AuthenticationProvider authenticationProvider, _) {
              switch (authenticationProvider.status) {
                case AuthenticationStatus.unauthenticated:
                  return TheRegisterPage();
                case AuthenticationStatus.authenticating:
                  return TheLoadingPage();
                case AuthenticationStatus.authenticated:
                  return MultiProvider(
                    providers: [
                      Provider<DatabaseProvider>(create: (_) => DatabaseProvider()),
                    ],
                    child: Consumer<PreferencesProvider>(
                        builder: (context, preferences, _) => MaterialApp(
                              home: TheGroupManagementPage(),
                              routes: <String, WidgetBuilder>{
                                TheGroupManagementPage.routeName: (BuildContext context) => TheGroupManagementPage(),
                                TheGroupCreationPage.routeName: (BuildContext context) => TheGroupCreationPage(),
                                TheGroupPage.routeName: (BuildContext context) => TheGroupPage(),
                                TheSettingsPage.routeName: (BuildContext context) => TheSettingsPage(),
                                TheProfilePage.routeName: (BuildContext context) => TheProfilePage(),
                                TheContactsPage.routeName: (BuildContext context) => TheContactsPage(),
                              },
                              theme: preferences.isDarkMode ? DarkTheme.themeData : LightTheme.themeData,
                              debugShowCheckedModeBanner: false,
                            )),
                  );
              }
            });
          });
        });
  }
Run Code Online (Sandbox Code Playgroud)

设置页面.dart

Switch(
  value: preferences.isDarkMode,
  onChanged: (isDarkmode) => preferences.isDarkMode = isDarkmode,
),
Run Code Online (Sandbox Code Playgroud)

Rém*_*let 7

你陷入了 XY 问题

这里真正的问题不是“我的小部件重建太频繁”,而是“当我的小部件重建时,我的应用程序返回到启动页面”。

解决方案不是防止重建,而是更改您的build方法以解决问题,这是我之前在此处详细介绍的内容:如何处理不需要的小部件构建?

您遇到了与交叉链接问题相同的问题:您误用了FutureBuilder.

不要

@override
Widget build(BuildContext context) {
  return FutureBuilder(
    // BAD: will recreate the future when the widget rebuild
    future: Future.delayed(new Duration(seconds: fakeDelayInSeconds)),
    ...
  );
}
Run Code Online (Sandbox Code Playgroud)

class Example extends StatefulWidget {
  @override
  _ExampleState createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  // Cache the future in a StatefulWidget so that it is created only once
  final fakeDelayInSeconds = Future<void>.delayed(const Duration(seconds: 2));

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      // Rebuilding the widget no longer recreates the future
      future: fakeDelayInSeconds,
      ...
    );
  }
}
Run Code Online (Sandbox Code Playgroud)


Fra*_*hez 5

使用 Consumer 时,每次通知侦听器时都会强制小部件重建。

为了避免这种行为,您可以按照 ian Villamia 的答案中所述使用 Provider.of,因为它可以在您需要的任何地方使用,并且仅在您需要的地方使用。

使用 Provider.of 的代码中的更改将在解析主题时删除使用者并添加 Provider.of,如下所示:

theme: Provider.of<PreferencesProvider>(context).isDarkMode ? DarkTheme.themeData : LightTheme.themeData,        
Run Code Online (Sandbox Code Playgroud)

但是,如果您想继续使用 Consumer,您可以做其他事情:

Consumer 小部件上的 child 属性是未重建的子属性。您可以使用它在那里设置TheSpashScreen,并通过构建器将其传递给materialApp。

翻译:博士

如果为了简单起见您只需要利用一个变量,请使用 Provider.of。

将 Consumer 与其子属性一起使用,因为子属性不会重建。<= 更好的性能

使用 Provider.of

class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
SystemChrome.setEnabledSystemUIOverlays([]);

return MultiProvider(
  providers: [
    ChangeNotifierProvider<PreferencesProvider>(create: (_) => PreferencesProvider()),
    ChangeNotifierProvider<ConnectionProvider>(
      create: (_) => ConnectionProvider(),
    ),
    ChangeNotifierProvider<AuthenticationProvider>(create: (_) => AuthenticationProvider()),
  ],
  child: Builder(
    builder: (ctx) {
        return MaterialApp(
          home: TheSpashPage(),
          theme: Provider.of<PreferencesProvider>(ctx).isDarkMode ? DarkTheme.themeData : LightTheme.themeData,
            );
          }),
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

使用消费者

class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
SystemChrome.setEnabledSystemUIOverlays([]);

return MultiProvider(
  providers: [
    ChangeNotifierProvider<PreferencesProvider>(create: (_) => PreferencesProvider()),
    ChangeNotifierProvider<ConnectionProvider>(
      create: (_) => ConnectionProvider(),
    ),
    ChangeNotifierProvider<AuthenticationProvider>(create: (_) => AuthenticationProvider()),
  ],
  child: Consumer<PreferencesProvider>(
    child: TheSpashPage(),
    builder: (context, preferences, child) {
        return MaterialApp(
          home: child,
          theme: preferences.isDarkMode ? DarkTheme.themeData : LightTheme.themeData,
          debugShowCheckedModeBanner: false,
            );
          }),
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望这对您有帮助!