Провайдер перестраивает виджет, но ничего не отображается до "горячего перезапуска"

Я создаю приложение для флаттера, и я получаю некоторые данные из будущего, у меня также есть те же данные с помощью changenotifier. Логика такова, что хотя у какого-то объекта нет данных, потому что он ожидает в будущем, он отображает вращающийся круг. Я уже сделал это в приложении, и у меня есть виджет Loading(), когда объект не получил данные. Проблема, с которой я столкнулся, заключается в том, что я получаю данные, но они ничего не отображают.

данные отображаются правильно, пока я не выполню горячее обновление приложения. заглавная R вместо строчной r. Разница в том, что он запускает приложение и удаляет все агрегированные данные.

когда это происходит, кажется, что данные заполняют объект, но я предполагаю, что он становится не нулевым значением [], который пуст, но не равен нулю и отображает данные "слишком быстро", это, в свою очередь, ничего не отображает для этого виджета, пока я не перезапущу "r", который показывает мне скриншот выше.

вот оскорбительный код.

import 'package:disc_t/Screens/LoggedIn/Classes/classTile.dart';
import 'package:disc_t/Screens/LoggedIn/Classes/classpage.dart';
import 'package:disc_t/Screens/LoggedIn/Classes/classpageroute.dart';
import 'package:disc_t/Services/database.dart';
import 'package:disc_t/models/user.dart';
import 'package:disc_t/shared/loading.dart';
import 'package:flutter/material.dart';
import 'package:morpheus/page_routes/morpheus_page_route.dart';
import 'package:provider/provider.dart';

class ClassList extends StatefulWidget {
  @override
  _ClassListState createState() => _ClassListState();
}

class _ClassListState extends State<ClassList> {
  @override
  void initState() {
    ClassDataNotifier classdatanotif =
        Provider.of<ClassDataNotifier>(context, listen: false);
    // final user = Provider.of<User>(context);
    // getTheClasses(classdatanotif);
    // List<ClassData> d = classes;
  }

  @override
  Widget build(BuildContext context) {
    ClassDataNotifier classdatanotif = Provider.of<ClassDataNotifier>(context);

    List<ClassData> cData = Provider.of<List<ClassData>>(context);



    bool rebd = false;

    Widget checker(bool r) {
      if (cData == null) {
        return Loading();
      } else {
        if (rebd == false) {
          setState(() {
            rebd = true;
          });

          rebd = true;

          return checker(rebd);

          // return Text("Still Loading");
        } else {
          return PageView.builder(
              scrollDirection: Axis.horizontal,
              itemCount: cData.length,
              // controller: PageController(viewportFraction: 0.8),
              itemBuilder: (context, index) {
                return Hero(
                  tag: cData[index],
                  child: GestureDetector(
                    onTap: () {
                      // Navigator.of(context).push(ClassPageRoute(cData[index]));
                      Navigator.push(
                          context,
                          MorpheusPageRoute(
                              builder: (context) =>
                                  ClassPage(data: cData[index]),
                              transitionToChild: true));
                    },
                    child: ClassTile(
                      classname: cData[index].classname,
                      description: cData[index].classdescription,
                      classcode: cData[index].documentID,
                    ),
                  ),
                );
              });
        }
      }
    }

    
    return checker(rebd);
  }
}

вот как реализован провайдер

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.

  // final DatabaseService ds = DatabaseService();
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        StreamProvider<User>.value(
          value: AuthService().user,
          // child: MaterialApp(
          //   home: Wrapper(),
          // ),
        ),
        ChangeNotifierProvider<ClassDataNotifier>(
          create: (context) => ClassDataNotifier(),
        ),
        FutureProvider(
          create: (context) => DatabaseService().fetchClassdata,
        )
      ],
      child: MaterialApp(home: Wrapper()),
    );
  }
}

и вот функция, которая запускается для получения данных

Future<List<ClassData>> get fetchClassdata async {
    QuerySnapshot snapshot = await classesCollection.getDocuments();

    List<ClassData> _classList = List<ClassData>();

    snapshot.documents.forEach((element) async {
      QuerySnapshot pre = await Firestore.instance
          .collection("Classes")
          .document(element.documentID)
          .collection("Pre")
          .getDocuments();

      List<Preq> _preList = List<Preq>();

      pre.documents.forEach((preClass) {
        Preq preqData = Preq.fromMap(preClass.data);

        if (preClass.data != null) {
          _preList.add(preqData);
        }
      });

      ClassData data =
          ClassData.fromMap(element.data, element.documentID, _preList);

      if (data != null) {
        _classList.add(data);
      }
    });

    return _classList;
  }

1 ответ

Решение

Думаю с логикой вашего провайдера все в порядке, проблема в строчке

snapshot.documents.forEach((element) async {
...
}

ForEach - это не будущее (то, что внутри него - будущее из-за асинхронности, а сам метод - нет), поэтому код запускается в первый раз, он достигает forEach, который выполняет свое будущее для каждого значения и распространяется на следующую строку code, возврат, но список пуст, потому что forEach еще не выполнен.

Для этого случая есть специальный Future.forEach, поэтому вы можете дождаться метода значения перед запуском следующей строки

Future<List<ClassData>> get fetchClassdata async {
QuerySnapshot snapshot = await classesCollection.getDocuments();

List<ClassData> _classList = List<ClassData>();

await Future.forEach(snapshot.documents, (element) async {
  QuerySnapshot pre = await Firestore.instance
      .collection("Classes")
      .document(element.documentID)
      .collection("Pre")
      .getDocuments();

  List<Preq> _preList = List<Preq>();

  pre.documents.forEach((preClass) {
    Preq preqData = Preq.fromMap(preClass.data);

    if (preClass.data != null) {
      _preList.add(preqData);
    }
  });

  ClassData data =
      ClassData.fromMap(element.data, element.documentID, _preList);

  if (data != null) {
    _classList.add(data);
  }
});

return _classList;
}

Вот аналогичная проблема с провайдером с forEach. Может быть, это поможет тебе немного лучше понять

Другие вопросы по тегам