AppWidget 主页在状态更改时不会更改
AppWidget home not changing when state changes
使用 Riverpod + StateNotifier,但我认为其他提供商也存在同样的问题。
我有一个身份验证 StateNotifier class 和 StateNotifierProvider,并将 MaterialApp 小部件包装到 Riverpod 消费者中,以便在用户不再经过身份验证时重建完整的 app/widget 树。
当我使用 pushReplacementNamed 导航到第三页并更新 authenticationStateNotifierProvider 的状态时,我可以看到包装应用程序的消费者的构建方法被触发并且状态被更新(打印(状态))但是主页和小部件树未重建。
具有 3 个屏幕的示例应用程序存在问题:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/all.dart';
void main() {
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends ConsumerWidget {
@override
Widget build(BuildContext context, ScopedReader watch) {
final state = watch(authenticationNotifier.state);
print(state);
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: state is Unauthenticated ? LoginScreen() : HomeScreen(),
onGenerateRoute: (RouteSettings settings) {
if (settings.name == '/second')
return MaterialPageRoute(builder: (_) => SecondScreen());
else
return MaterialPageRoute(builder: (_) => HomeScreen());
},
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('HomeScreen'),
),
body: Column(
children: [
MaterialButton(
child: Text('Logout'),
onPressed: () => context.read(authenticationNotifier).toggle(),
),
MaterialButton(
child: Text('Second'),
onPressed: () => Navigator.pushReplacementNamed(
context,
'/second',
),
),
],
),
);
}
}
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('SecondScreen'),
),
body: MaterialButton(
child: Text('Logout'),
onPressed: () => context.read(authenticationNotifier).toggle(),
),
);
}
}
class LoginScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('LoginScreen'),
),
body: MaterialButton(
child: Text('Login'),
onPressed: () => context.read(authenticationNotifier).toggle(),
),
);
}
}
// Controller.
final authenticationNotifier =
StateNotifierProvider((ref) => AuthenticationNotifier());
class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
AuthenticationNotifier() : super(Unauthenticated());
void toggle() {
state = state is Unauthenticated ? Authenticated() : Unauthenticated();
}
}
// State.
abstract class AuthenticationState {}
class Authenticated extends AuthenticationState {}
class Unauthenticated extends AuthenticationState {}
如果您测试应用程序,您会看到状态管理在登录和主页之间按照代码预期的方式工作,但是一旦您导航到主页的第二个屏幕并按下注销按钮,状态就会改变在 App 上 widged 但 widget tree 没有更新。
问题出在你的 onGenerateRoute
你总是在你的其他情况下重定向到主屏幕。 MaterialApp 上的 home
属性 仅在应用首次打开时调用。因此,要真正解决您的问题(我看到了这个 commit in your repo,如果您的用户会话在外部无效,这是一种解决方法,它不会起作用),您应该添加如下内容:
home: state is Unauthenticated ? LoginScreen() : HomeScreen(),
onGenerateRoute: (RouteSettings settings) {
if (settings.name == '/second')
return MaterialPageRoute(builder: (_) => SecondScreen());
else
return MaterialPageRoute(builder: (_) => state is Unauthenticated ? LoginScreen() : HomeScreen());
},
我认为这对你有用。
使用 Riverpod + StateNotifier,但我认为其他提供商也存在同样的问题。
我有一个身份验证 StateNotifier class 和 StateNotifierProvider,并将 MaterialApp 小部件包装到 Riverpod 消费者中,以便在用户不再经过身份验证时重建完整的 app/widget 树。
当我使用 pushReplacementNamed 导航到第三页并更新 authenticationStateNotifierProvider 的状态时,我可以看到包装应用程序的消费者的构建方法被触发并且状态被更新(打印(状态))但是主页和小部件树未重建。
具有 3 个屏幕的示例应用程序存在问题:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/all.dart';
void main() {
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends ConsumerWidget {
@override
Widget build(BuildContext context, ScopedReader watch) {
final state = watch(authenticationNotifier.state);
print(state);
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: state is Unauthenticated ? LoginScreen() : HomeScreen(),
onGenerateRoute: (RouteSettings settings) {
if (settings.name == '/second')
return MaterialPageRoute(builder: (_) => SecondScreen());
else
return MaterialPageRoute(builder: (_) => HomeScreen());
},
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('HomeScreen'),
),
body: Column(
children: [
MaterialButton(
child: Text('Logout'),
onPressed: () => context.read(authenticationNotifier).toggle(),
),
MaterialButton(
child: Text('Second'),
onPressed: () => Navigator.pushReplacementNamed(
context,
'/second',
),
),
],
),
);
}
}
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('SecondScreen'),
),
body: MaterialButton(
child: Text('Logout'),
onPressed: () => context.read(authenticationNotifier).toggle(),
),
);
}
}
class LoginScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('LoginScreen'),
),
body: MaterialButton(
child: Text('Login'),
onPressed: () => context.read(authenticationNotifier).toggle(),
),
);
}
}
// Controller.
final authenticationNotifier =
StateNotifierProvider((ref) => AuthenticationNotifier());
class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
AuthenticationNotifier() : super(Unauthenticated());
void toggle() {
state = state is Unauthenticated ? Authenticated() : Unauthenticated();
}
}
// State.
abstract class AuthenticationState {}
class Authenticated extends AuthenticationState {}
class Unauthenticated extends AuthenticationState {}
如果您测试应用程序,您会看到状态管理在登录和主页之间按照代码预期的方式工作,但是一旦您导航到主页的第二个屏幕并按下注销按钮,状态就会改变在 App 上 widged 但 widget tree 没有更新。
问题出在你的 onGenerateRoute
你总是在你的其他情况下重定向到主屏幕。 MaterialApp 上的 home
属性 仅在应用首次打开时调用。因此,要真正解决您的问题(我看到了这个 commit in your repo,如果您的用户会话在外部无效,这是一种解决方法,它不会起作用),您应该添加如下内容:
home: state is Unauthenticated ? LoginScreen() : HomeScreen(),
onGenerateRoute: (RouteSettings settings) {
if (settings.name == '/second')
return MaterialPageRoute(builder: (_) => SecondScreen());
else
return MaterialPageRoute(builder: (_) => state is Unauthenticated ? LoginScreen() : HomeScreen());
},
我认为这对你有用。