【问题标题】:AppWidget home not changing when state changes状态更改时 AppWidget 主页不会更改
【发布时间】:2020-12-16 16:32:18
【问题描述】:

使用 Riverpod + StateNotifier,但我认为其他提供商也存在同样的问题。

我有一个身份验证 StateNotifier 类和 StateNotifierProvider 并将 MaterialApp 小部件包装到 Riverpod Consumer 中,以便在用户不再经过身份验证时重建完整的应用程序/小部件树。

当我使用 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 {}

如果您测试应用程序,您将看到登录和主页之间的状态管理与代码预期的一样,但是一旦您从主页导航到第二个屏幕并按下注销按钮,状态就会在应用 widged 但小部件树未更新。

【问题讨论】:

  • 因为你使用statelessWidget。请改用statefulWidget
  • 我使用 statelessWidget 是因为我使用 Consumer 来重建小部件并管理状态。实际上,如果我在身份验证状态更改时调试构建方法正在执行。问题是 MaterialApp 没有使用新的主页小部件重新创建小部件树。
  • 您的 authenticationStateNotifierProvider 的状态是什么类型? (state is Authenticated) 看起来很奇怪,不像枚举,所以只是想知道这是否是问题所在。即,您的条件写得是否正确?
  • 我已经用工作示例代码编辑了这个问题来演示这个问题。在 pubspec 中使用 state_notifier 和 flutter_riverpod。示例应用也在github.com/abibiano/riverpod_test

标签: flutter riverpod


【解决方案1】:

问题在于您的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());
},

我认为这应该适合你。

【讨论】:

  • 感谢您的回答。在更改状态之前使用您的建议并在第二个屏幕上添加 Navigator.pushReplacementNamed(context, '/', ) 解决了我的问题。
猜你喜欢
  • 2015-05-16
  • 1970-01-01
  • 1970-01-01
  • 2021-12-25
  • 1970-01-01
  • 1970-01-01
  • 2019-11-09
  • 2023-04-09
  • 2020-01-25
相关资源
最近更新 更多