Fluter小部件未运行initState并将其正确传递到小部件中

时间:2020-06-01 20:18:42

标签: flutter dart

我有下面的代码,其想法是,当用户登录时,他们被推送到该页面(“仪表板”)。我正在尝试使用initState来使应用程序运行函数takenSurvey2(),该函数然后为布尔takenSurvey创建一个值。这来自查询Cloud Firestore。

布尔值takenSurvey然后将在堆栈的“位置”小部件中使用,以确定要向用户显示的图标(如果用户未参加调查(即takenSurvey =否)他们会看到一个“新”图标。

但是,当前发生的情况是,当用户登录并加载页面并显示错误图标时,该小部件认为takenSurvey的值不为true或false。但是,当我创建一个任意按钮并使用OnPressed打印takenSurvey的值时,它在控制台中显示为“ false”。如我所料,当我热重新加载仪表板页面时,错误图标变成了“新”图标-为什么它不立即这样做?

我不确定自己在做什么,以及为什么小部件看不到takenSurvey的值,即使takenSurvey2()已在initState中运行,并且我已经将它包装在Builder小部件中了吗?

      class Dashboard extends StatefulWidget {
      static const String id = 'dashboard';
      @override
      _DashboardState createState() => _DashboardState();
    }

    class _DashboardState extends State<Dashboard> {
      final _auth = FirebaseAuth.instance;
      var userid;
      String surveyName;
      bool takenSurvey;
      String title;

 @override
  void initState() {
    super.initState();

    getCurrentUser();
    getUserID();
    currentSurveyName();
    takenSurvey2();
  }

void takenSurvey2() async {
    final snapShot = await Firestore.instance
        .collection('$surveyName' + '_entrants')
        .document(userid)
        .get();
    if (snapShot.exists) {
      takenSurvey = true;
    } else {
      takenSurvey = false;
    }
  }

 @override   Widget build(BuildContext context) {
      body: SingleChildScrollView(
        child: Container(
          padding: EdgeInsets.symmetric(horizontal: 24.0),
          child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              children: <Widget>[
               Stack(
                  alignment: Alignment.center,
                  children: <Widget>[
                    Container(
                      width: MediaQuery.of(context).size.width * 0.9,
                      child: SquareButton(
                          buttonTitle: "",
                          fontSize: 36,
                          onPressed: () async {
                            final snapShot = await Firestore.instance
                                .collection('$surveyName' + '_entrants')
                                .document(userid)
                                .get();
                            if (snapShot.exists) {
                              _alreadyCompletedSurveyError();
                            } else if (surveyName.isEmpty)
                              _otherError();
                            else {
                              return Navigator.push(
                                  context,
                                  new MaterialPageRoute(
                                      builder: (BuildContext context) =>
                                          new TestSurvey(surveyName)));
                            }
                          }),
                    ),
                    Positioned(
                      right: 30,
                      top: 20,
                      child: Builder(builder: (context) {
                        if (takenSurvey == false) {
                          return Icon(
                            Foundation.burst_new,
                            size: 48,
                            color: Color(0xff303841),
                          );
                        } else if (takenSurvey == true) {
                          return Icon(
                            Foundation.check,
                            size: 48,
                            color: Color(0xff303841),
                          );
                        } else
                          return Icon(
                            MaterialIcons.error,
                            size: 48,
                            color: Color(0xff303841),
                          );
                      }),
                    ),
                    Container(
                      child: Text('Latest Survey',
                          style: TextStyle(
                              color: Colors.white,
                              fontSize: 36,
                              fontFamily: "Roboto")),

2 个答案:

答案 0 :(得分:1)

首先请注意,小部件的build()方法是同步的,因此它可以在Flutter需要时立即运行,并且可以在框架需要时被调用多次,而不必调用一次

其次,您的takedSurvey没有默认值,并且初始化为空

那么接下来会发生什么:

  1. 当您的窗口小部件进入树时,initState被称为
  2. 您的异步例程开始
  3. 框架调用
  4. build()方法并渲染第一帧
  5. takenSurvey == null,这就是显示错误图标的原因
  6. 有时,您的异步例程完成(永远不会在第一帧之前完成),并且takeSurvey值设置为true / false
  7. 缺少的步骤-通知Flutter例程已完成,我们有一些数据要显示

按下按钮或进行热重装怎么办?如果您在例行程序完成后进行了此操作(如果您之前按过该按钮,则会看到此情况

热重装会重建树,最后您可以在屏幕上看到数据

对代码的第一个最简单的解决方法是在void takeSurvey2()例程的结尾处调用setState()-这将重建您的小部件(flutter重新运行build())

void takenSurvey2() async {
   final snapShot = await Firestore.instance
      .collection('$surveyName' + '_entrants')
      .document(userid)
      .get();
   if (snapShot.exists) {
     takenSurvey = true;
   } else {
     takenSurvey = false;
   }
   setState((){});
}

当然,您需要处理takenSurvey == null-您需要向用户显示“正在加载”指示器,因为您执行了一些异步操作,但用户不知道发生了什么

第二种更方便的方法是使用FutureBuilder(完全由您来完成下一个代码片段以使其起作用,可以随意在评论中提问)

    FutureBuilder(
        future: Firestore.instance
          .collection('$surveyName' + '_entrants')
          .document(userid)
          .get(),
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            if (snapshot.data.exists) {
              _alreadyCompletedSurveyError();
            } else if (surveyName.isEmpty)
              _otherError();
              ... more code
          } else {
            // show loading
          }
        },
      )

最后一条建议-您需要学习状态管理以及如何将逻辑与小部件分开。

您将以开发人员的方式使用它。

从这里开始 https://flutter.dev/docs/development/data-and-backend/state-mgmt

答案 1 :(得分:-1)

您可以尝试在initstate中添加异步功能吗?

 @override
  void initState() {
    super.initState();
    test();
  }

void test() async {
    await getCurrentUser();
    await getUserID();
    await currentSurveyName();
    await takenSurvey2();
}