首页 > 解决方案 > 在 null 上调用了方法“数据”。接收方:null 尝试调用:data()

问题描述

当我尝试将添加到 Firebase 的值放入 Droppdown 按钮时,出现以下错误

在 null 上调用了方法“数据”。接收者:nullTried 调用:数据()

我认为错误的原因是我无法将值从 Todo.dart 调用到 post_page.dart。

使用 debag,post_page.dart 中的 todolist 为空。

如果您能回答,我将不胜感激。

post_page.dart

import 'package:cloud_firestore/cloud_firestore.dart';
import "package:flutter/material.dart";
import 'package:habit_boddy/component/post/task_add_button.dart';
import 'package:habit_boddy/utils/constants.dart';
import 'package:habit_boddy/view/common/components/drop_text.dart';
import 'package:habit_boddy/view/common/components/post_caption_part.dart';
import 'package:habit_boddy/view/post/page/Todo.dart';
import 'package:habit_boddy/view/post/page/detail_page.dart';
import 'package:habit_boddy/view/post/page/picture_page.dart';
import 'package:habit_boddy/view/post/page/task_setting.dart';
import 'package:habit_boddy/view_models/post_view_model.dart';
import 'package:habit_boddy/view_models/todo_view_model.dart';
import 'package:provider/provider.dart';
import 'confirm_dialog.dart';


class PostPage extends StatefulWidget {
  @override
  _PostPageState createState() => _PostPageState();
}

class _PostPageState extends State<PostPage> {
  final _captionController = TextEditingController();

  @override
  void initState() {
    _captionController.addListener(_onCaptionUpdated);
    super.initState();
  }

  @override
  void dispose() {
    _captionController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<ToDoViewModel>(
      create:(_) => ToDoViewModel()..getRealtime(),
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.amber,
          actions: [
            ElevatedButton(
                child: Text('Post'),
                style: ElevatedButton.styleFrom(
                  primary: Colors.orange,
                  onPrimary: Colors.white,
                ),
                onPressed: () => showConfirmedDialog(
                    context: context,
                    title: "Post",
                    content: "May I post?",
                    onConfirmed: (isConfirmed) {
                      if (isConfirmed) {
                        _post(context);
                      }
                    }))
          ],
        ),
        body: Consumer<ToDoViewModel>(builder: (context, model, child) {
          final todoList = model.todoList;
          return DropdownButton<Todo>(
            items: todoList.map((todo) =>
              DropdownMenuItem<Todo>(
                value: Todo(title: todo.title),
                child: Text(todo.title),
              )
            ).toList(),
          );
        }),
        floatingActionButton:
            Consumer<ToDoViewModel>(builder: (context, model, child) {
          return FloatingActionButton(
            onPressed: () async {
              await Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => TaskSetting()),
              );
            },
            child: Icon(Icons.add),
          );
        }),
      ),
    );
  }

  _onCaptionUpdated() {
    final viewModel = Provider.of<PostViewModel>(context, listen: false);
    viewModel.caption = _captionController.text;
    print("caption: ${viewModel.caption}");
  }

  void _post(BuildContext context) {
    final postViewModel = Provider.of<PostViewModel>(context, listen: false);
  }
}

Widget build(BuildContext context) {
  final postViewModel = Provider.of<PostViewModel>(context);
  return Container(
    child: Padding(
      padding: const EdgeInsets.all(40.0),
      child: SingleChildScrollView(
        child: Column(
          children: [
            Row(
              children: [
                Expanded(
                  flex: 3,
                  child: Container(
                      child: Padding(
                        padding: const EdgeInsets.only(left: 10.0),
                        child: DropText(),
                      ),
                      height: 230),
                ),
                Expanded(
                    flex: 1,
                    child: Padding(
                      padding: const EdgeInsets.only(bottom: 155.0),
                      child: TaskAdd(),
                    )),
              ],
            ),
            Container(
                child: Padding(
                  padding: const EdgeInsets.only(top: 20.0),
                  child: DetailPost(),
                ),
                height: 270),
            Row(
              children: [
                PicturePage(),
                postViewModel.imageFile == null
                    ? Container()
                    : Container(
                        height: 60,
                        width: 60,
                        child: SingleChildScrollView(
                          child: PostCaptionPart(
                            from: PostCaptionOpenMode.FROM_POST,
                          ),
                        ),
                      )
              ],
            ),
          ],
        ),
      ),
    ),
  );
}

待办事项

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:habit_boddy/view_models/todo_view_model.dart';

class Todo {
  final ToDoViewModel model;
  String title = "";
  DateTime createdAt;
  Todo({DocumentSnapshot doc,  this.title, this.model, this.createdAt}){
    this.title = doc.data()["title"];
    final Timestamp timestamp = doc.data()['createdAt'];
    this.createdAt = timestamp.toDate();
  }
}

标签: flutterdart

解决方案


我相信这条线导致了错误。

              DropdownMenuItem<Todo>(
                value: Todo(title: todo.title),

您在 Todo 构造函数中调用 doc.data() 而不传递 doc 变量,因此在 null 变量上调用 .data() 。相反,您应该先获取标题,然后在实例化 Todo 对象时将其传递给构造函数。

如果你真的需要传递 doc 变量,比如说你是否需要它来做其他事情,在构造函数中使它成为必需并断言它不为空。

Todo({@required DocumentSnapshot doc,  this.title, this.model, this.createdAt}) :  assert(doc != null){
...
}

您还应该研究 Dart 空安全性,这将有助于避免此类问题。


推荐阅读