0

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

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

4

1 回答 1

0

问题在于您onGenerateRoute在其他情况下总是重定向到 HomeScreen。MaterialApp 上的home属性仅在应用首次打开时调用。因此,要真正解决您的问题(我在您的 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());
},

我认为这应该对你有用。

于 2020-12-17T17:47:46.107 回答