【问题标题】:How to structure app state so all dependent widgets update when state changes?如何构建应用程序状态,以便所有依赖的小部件在状态更改时更新?
【发布时间】:2019-07-03 16:31:36
【问题描述】:

我在 SharedPreferences 中保留了多个帐户,并希望使当前帐户(用户在对话框中选择)可在整个应用程序中访问。当用户更改当前帐户时,UI 应自动更新以显示该帐户。但是,我尝试过的应用状态系统不会在当前用户更改时更新我的​​ StatefulWidgets。

我尝试过 SharedPreferences、InheritedWidget、Provider 和 ChangeNotifier。我一直无法监听 SharedPreferences 的变化,并且其他解决方案在状态变化时不会更新 UI。

// Main.dart

void main() => runApp(
  ChangeNotifierProvider<AppStateManager>.value(
    value: AppStateManager(),
    child: MyApp()
  )
);
class AppStateManager extends ChangeNotifier {
  int _currentStudentIndex;
  int get currentStudentIndex => _currentStudentIndex;

  set currentStudentIndex(int index) {
    _currentStudentIndex = index;
    notifyListeners();
  }
}
// Code that runs when the user selects a new account

onPressed: () {
  Provider.of<AppStateManager>(context).currentStudentIndex = index;
  Navigator.pop(context);
},
// The state for my StatefulWidget 

_TodayState() {
    getCurrentStudent().then((student) => setState(() {
      _currentStudent = student;
    }));
}

Future<Student> getCurrentStudent() async {
  List<String> students = await PreferencesManager.getStudents();

  final AppStateManager stateManager = Provider.of<AppStateManager>(context);

  Map<String, dynamic> currentStudent = jsonDecode(students[stateManager.currentStudentIndex ?? 0]);

  return Student.fromJson(currentStudent);
}

【问题讨论】:

  • 如果需要,有多种方法可以强制重新绘制整个应用程序:stackoverflow.com/questions/43778488/…
  • 当学生改变时,学生的顶级提供者不会导致整个子树的完全重建吗? pub.dev/packages/provider
  • 感谢您的回复!用户在对话框中选择一个新学生,更新提供者中的当前学生索引。我认为小部件树应该重建,但我在 _TodayState 初始化程序中的断点没有激活。
  • 可能是在对话框而不是常规小部件中更新提供程序值?

标签: flutter dart


【解决方案1】:

我尝试从提供的 sn-ps 重新创建您的代码,并且可以重现您的问题 (here)。您正确设置状态并调用notifyListeners(),但您没有在构建方法中使用状态。您假设每次看到 _TodayState 时都会构造它,但事实并非如此。例如,如果 Flutter 在一堆活动中(例如,当您将导航器路由推送到其顶部时),则将其保存在内存中。

换句话说,您的构造函数代码 (getCurrentStudent().then(...)) 没有像您想象的那样经常执行。

为确保您的 UI 得到更新,请将内容放入 build() 方法中。例如:

Consumer<AppStateManager>(
  builder: (context, manager, child) => FutureBuilder<Student>(
    future: getCurrentStudent(manager.currentStudentIndex),
    builder: (context, snapshot) {
      if (!snapshot.hasData) {
        return const CircularProgressIndicator();
      }
      return Text('Student: ${snapshot.data.name}');
    },
  ),
),

这行得通。 Here 是要点。每次状态变化时它都会获取学生,这可能是也可能不是你想要的。

如果您绝对需要混合使用 setState、futures 和 InheritedWidgets,您可能需要手动收听提供的值(使用类似 stateManager.addListener(listener) 的东西)。但最好避免这种情况,因为它可能是错误的来源。

作为 Provider 的一般建议:在构建方法中收听它(使用 Consumer&lt;...&gt;Provider.of&lt;...&gt;),而不是其他任何地方。

【讨论】:

    猜你喜欢
    • 2021-07-25
    • 2019-12-06
    • 2020-03-18
    • 2019-10-06
    • 2021-06-11
    • 1970-01-01
    • 2016-10-14
    • 2020-04-17
    • 2018-05-21
    相关资源
    最近更新 更多