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)
你陷入了 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)
使用 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)
我希望这对您有帮助!
| 归档时间: |
|
| 查看次数: |
2355 次 |
| 最近记录: |