_InternalLinkedHashMap не является подтипом пользовательского класса в приведении типа. Получение массива карт из Firestore - PullRequest
0 голосов
/ 24 февраля 2020

Я храню данные из пользовательского класса "PollOption" в firestore в виде массива карт. Когда я пытаюсь получить данные, я получаю эту ошибку

_InternalLinkedHashMap<String, dynamic> is not a subtype of 'PollOption' in type cast.

Мои другие данные извлекаются нормально.

Моя модель, которая сохраняется в коллекции , PollOption вкладывается в это:

class Question{
  //single values
  final Timestamp askedOn;
  final String chosenAnswer;
  final String askedBy;
 ...

  //arrays
  final List<String> categories;
  final List<String> declinedUsers;
  final List<PollOption> pollOptions;
 ...


  Question({
    this.askedBy,
    this.askedByAvatar,
    this.askedByName,

...

    this.pollOptions,
    this.starredMessages,

...

  });

Моя модель PollOption:

class PollOption{
  final String option;
  final bool selected;
  final int optionNumber;
  final int votes;

  PollOption({this.option, this.optionNumber, this.selected, this.votes});
}

Как я храню данные в Firestore:

Future createQuestion(Question data) async {
    ...

    List<Map> sterilizedPolls (){
      List<Map> polls = [];
      if(data.pollOptions != null){
      data.pollOptions.forEach((PollOption pollOption){
        Map option ={
          'option': pollOption.option,
          'votes': pollOption.votes,
          'optionNumber': pollOption.optionNumber,
          'selected': pollOption.selected,
        };
        polls.add(option);
      });

      }
      return polls;

    }


    return await questionCollection.add({ 
    'askedBy': uid,
...
    'declinedUsers': [],
    'pollOptions': sterilizedPolls(),

...


    });

  }

Как я Получаю данные:

//Getting all the questions I want
Future<List<Question>> getOpenQuestions() async{
    final QuerySnapshot result = await questionCollection
      .where('locked', isEqualTo: false)
      .where('chosenAnswer', isEqualTo: '')
      .getDocuments();
    final List<DocumentSnapshot> documents = result.documents;
    final List<Question> openQuestions = [];
   documents.forEach((data){
     print('accessing database');
      openQuestions.add(
        Question(
          question: data['question'],

        )
      );
    });

    return openQuestions;
  }

//Creating list from snapshot
List<Question> _questionListFromSnapshot(QuerySnapshot snapshot) {



    return snapshot.documents.map((doc){

      return Question(
        askedBy: doc.data['askedBy'] ?? '',
        askedByName: doc.data['askedByName'] ?? 'Anonymous',

     ...

        pollOptions: List.from(doc.data['pollOptions']).cast<PollOption>() ?? [],

       ...

      );
    }).toList();





  }

//creating stream
   Stream<List<Question>> get openQuestionList {
     try {
      return questionCollection
      .where('locked', isEqualTo: false)
      .snapshots()
      .map(_questionListFromSnapshot);

     } catch (e) {
       return e;
     }

  }

И как я отображаю данные:

class OpenQuestions extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    final openQuestions = Provider.of<List<Question>>(context) ?? [];

    return StreamBuilder<List<Question>>(
        stream: QuestionDatabaseService().openQuestionList,
        builder: (context, snapshot){
          if(snapshot.hasError){
            return Text('Error!');
          }
          if (snapshot.hasData){
              return ListView.builder(
                itemCount: openQuestions.length,
                itemBuilder: (context, index) {
                  return QuestionTile(question: openQuestions[index], context: context);
                },
              );
          }else{
            return Loading();
          }

        }

        );

//The QuestionTile Loaded

class QuestionTile extends StatelessWidget {

  final Question question;
  final context;

  QuestionTile({this.question, this.context});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(10.0),
      child: Card(
        child: ListTile(
         ...
          title: Text(question.question),
          subtitle: question.poll == false ? Text(question.message) : Text('Poll'),
          onTap: (){
            _showBottomModal(context, question);
          },
        ),
      ),

    );
  }
}

//The bottom modal that pops up
void _showBottomModal(context, question)async {
  showModalBottomSheet(context: context, isScrollControlled: true, builder: (context){
    return Container(
      //color: Color(0xFF737373),
      child: Container(
        child: FractionallySizedBox(

//Where the problem widget is being called

          child: AnswerQuestion(question: question),
          heightFactor: 0.90,
          ),
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.only(
            topLeft: const Radius.circular(30.0),
            topRight: const Radius.circular(30.0),
          )
        ),

      ),
    );
  });
}

//AnswerQuestion widget where the error is occurring

class _AnswerQuestionState extends State<AnswerQuestion> {

...



  @override
  Widget build(BuildContext context) {



    return Container(

      child:Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
...
          Form(
            key: _formKey,
            child: Column(
              children: [
//Where the data is being displayed with error

                ConstrainedBox(
                constraints: BoxConstraints(
                 maxHeight: 300.0
                 ),
              child: Container(
               child: ListView.builder(
                    itemCount: widget.question.pollOptions.length,
                   itemBuilder: (BuildContext context, int index){



                   return ListTile(
                      title: Text(widget.question.pollOptions[index].option),
                     leading: Radio(
                     value: widget.question.pollOptions[index].optionNumber, 
                      groupValue: _optionSelected, 
                      onChanged: _handleRadioValueChange,
                   ),
                 );

                ...

Извините, если слишком много кода (или недостаточно), я все еще изучал флаттер и хотел предоставить как можно больше, чтобы помочь с этой ошибкой.

1 Ответ

1 голос
/ 24 февраля 2020

Проблема со строкой:

pollOptions: List.from(doc.data['pollOptions']).cast<PollOption>() ?? [],

Значение в doc.data['pollOptions'] на самом деле JSON данных, которые представлены в дротике как Map<String, dynamic>. Дарт не знает, как автоматически конвертировать карту в ваш объект PollOption.

Рассмотрим это вместо этого:

pollOptions: doc.data['pollOptions'].map((Map<String, dynamic> json) => PollOption.fromJson(json)).toList()

Предполагается, что doc.data['pollOptions'] возвращает список, который следует делать, если это массив JSON.

И, конечно, вам нужен fromJson фабричный конструктор для вашего PollOption класса:

class PollOption {
...
   factory PollOption.fromJson(final Map<String, dynamic> json) {
      return PollOption(json["optionkey"], etc);
   }
...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...