如何构造应用程序状态,以便状态变化时所有依赖的小部件都更新?

时间:2019-07-03 16:31:36

标签: flutter dart

我有几个保留在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);
}

1 个答案:

答案 0 :(得分:2)

我尝试通过提供的代码段重新创建您的代码,并且可能会重现您的问题(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,future和InheritedWidgets,则可能需要手动监听提供的值(使用类似stateManager.addListener(listener)的值)。但是最好避免这种情况,因为它可能是错误的来源。

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