当我渲染我的子材料应用程序时,如何阻止我的更改通知提供程序重建我的父材料应用程序?
Posted
技术标签:
【中文标题】当我渲染我的子材料应用程序时,如何阻止我的更改通知提供程序重建我的父材料应用程序?【英文标题】:How can I stop my change notifier provider from rebuilding my parent material app when I am rendering my child material app? 【发布时间】:2020-07-19 15:11:04 【问题描述】:我有一个应用程序类,它返回一个MaterialApp()
,它的主页设置为TheSplashPage()
。如果有任何偏好更改,此应用程序会侦听偏好通知。
然后在TheSplashPage()
中,我等待一些条件为真,如果它们为真,我向它们展示我的嵌套材质应用程序。
旁注:我在这里使用材质应用程序,因为它具有父材质应用程序不应该具有的路由,因此看起来更合乎逻辑。而且,一旦用户未经身份验证或断开连接,我希望整个嵌套应用程序关闭并显示另一个页面。这很好用!
但我的问题如下。两个应用程序都收听ThePreferencesProvider()
,因此当主题更改时,它们都会收到通知并重建。但这是一个问题,因为每当父材质应用程序重建时,它都会返回启动页面。所以现在每当我更改TheSettingsPage()
上的设置时,我都会回到TheSplashPage()
。
所以我的问题是,当我更改设置时,如何阻止我的应用程序返回 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,
);
),
);
TheSplashPage.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,
)),
);
);
);
);
TheSettingsPage.dart
Switch(
value: preferences.isDarkMode,
onChanged: (isDarkmode) => preferences.isDarkMode = isDarkmode,
),
【问题讨论】:
【参考方案1】:你爱上了 XY 问题
这里真正的问题不是“我的小部件重建过于频繁”,而是“当我的小部件重建时,我的应用返回到启动页面”。
解决方案不是防止重建,而是更改您的 build
方法以解决问题,这是我之前在此处详细介绍的内容:How to deal with unwanted widget 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)),
...
);
做:
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,
...
);
【讨论】:
【参考方案2】:使用 Consumer 时,每次通知侦听器时都会强制重新构建小部件。
为避免此类行为,您可以按照 ian villamia 的回答中所述使用 Provider.of,因为它可以在您需要的任何地方使用,并且仅在您需要的地方使用。
您的代码中使用 Provider.of 的更改将是在解决主题时删除消费者并添加 Provider.of,如下所示:
theme: Provider.of<PreferencesProvider>(context).isDarkMode ? DarkTheme.themeData : LightTheme.themeData,
但是如果你想继续使用 Consumer,你可以做点别的:
Consumer 小部件上的子属性是 未重建的子属性。您可以使用它在那里设置 TheSpashScreen,并通过构建器将其传递给 materialApp。
TL:DR
如果您为了简单起见只需要利用一个变量,请使用 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,
);
),
);
使用消费者
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,
);
),
);
希望对你有帮助!
【讨论】:
【参考方案3】:基本上有两种使用提供者的方法
一个是您当前使用的消费者类型, 正在使用提供者的实例
final _preferencesProvider= Provider.of<PreferencesProvider>(context, listen: false);
如果您希望在调用 notifyListeners() 时重建小部件,则可以切换“listen:true”...否则为 false 也可以像任何其他实例一样使用 _preferencesProvider.someValue
【讨论】:
以上是关于当我渲染我的子材料应用程序时,如何阻止我的更改通知提供程序重建我的父材料应用程序?的主要内容,如果未能解决你的问题,请参考以下文章