NoSuchMethodError: метод ‘query’ был вызван при null. (Хотя он переносит элементы в пользовательский интерфейс)

#flutter #dart

#flutter #dart

Вопрос:

Подведение итогов проблемы

Я был в stack и много видел этот вопрос, я перепробовал все решения, которые мог найти, но у меня это не сработало. Я извлекаю некоторые данные из своей sqflite базы данных, и иногда он извлекает результаты, а иногда и нет, что кажется довольно странным. Я читал, что вы не должны вызывать init() функцию в конструкторе Database .

Говорят, что это неправильно

 import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';

class TaskDbProvider {
  Database db;
  
  TaskDbProvider(){
    init()
  }

  init() async {...}
}
 

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

Их решение

Прослушивание Completer() потока

 class TaskDbProvider {
  Database db;
  var readyCompleter = Completer();
  Future get ready => readyCompleter.future;

  TaskDbProvider(){
    init().then((_) {
      // mark the provider ready when init completes
      readyCompleter.complete();
    });
  }
}
 

Хотя это запускает новую цепочку исключений, которые я мог бы предоставить при необходимости.

ссылка на github

Ошибка

 [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: NoSuchMethodError: The method 'query' was called on null.
E/flutter (29065): Receiver: null
E/flutter (29065): Tried calling: query("Task")
E/flutter (29065): #0      Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
E/flutter (29065): #1      TaskDbProvider.fetchTaskList (package:oppo/resources/db_provider.dart:51:47)
 

Строка 51

 Future<List<Task>> fetchTaskList() async{
    List<Map<String,dynamic>> list = await db.query('Task'); //line 51 (db is null?)

    return List.generate(list.length, (i) {
      return  Task.fromDb(list[i]);
    });
  }
 

Как я инициализирую БД

Создание блока (некоторые дополнительные функции будут добавлены позже)

 class BlocSpeech {

  final cache =  TaskDbProvider();
}
 

Сделайте его доступным через InheritedWidget (он же поставщик)

 class SpeechProvider extends InheritedWidget{

  final BlocSpeech bloc ;
  static BlocSpeech of(BuildContext context){
    return (context.inheritFromWidgetOfExactType(SpeechProvider) as SpeechProvider).bloc;
  }
  SpeechProvider({Key key, Widget child})
      : bloc = BlocSpeech(),
        super(key: key,child: child);
  @override
  bool updateShouldNotify(covariant InheritedWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return true;
  }
  
}
 

Виджеты пользовательского интерфейса

Оберните виджет поставщиком

  SpeechProvider(child: Work())
 

и создайте виджет:

 
class Work extends StatefulWidget {
  @override
  _Work createState() => _Work();
}

class _Work extends State<Work> {
  List<Task> allTasks=[];


  @override
  Widget build(BuildContext context) {
    final SpeechBloc = SpeechProvider.of(context); //initialize the BLoC 
    final Future<List<Task>> future = SpeechBloc.cache.fetchTaskList(); //wait to fetch the items
    future.then((value) => allTasks=value); //assign them to local variable

    return ListView.builder(
        padding: const EdgeInsets.all(8),
        itemCount: (allTasks.length),
        itemBuilder: (BuildContext context, int index) {
          return Container(
              margin: EdgeInsets.only(top: 10, bottom: 10),
              child: _taskWidget(index,SpeechBloc));
        });
  }

 

При необходимости я добавлю любую дополнительную информацию.

Ответ №1:

«Вызывается при null» обычно означает, что вы поторопились с будущим вызовом. Убедитесь, что все в API, в котором указано «возвращает будущее» или «асинхронно», находится в методе, который возвращает будущее, и каждый вызов метода, который возвращает будущее, защищен ожиданием. Есть некоторые места, которые вы можете опустить, если разберетесь более тщательно, но это хороший первый шаг.

Комментарии:

1. Я обернул ListView.Builder с помощью StreamBuilder, данные получены, и они каким-то образом становятся нулевыми. snapshot.hasData is true (извлекает элементы), а затем это false . Может ли быть так, что это проблема рендеринга с StatefulWidget?

2. Возможно, вы перестроите поток во что-то другое, кроме initState?

3. Я вызываю его внутри метода виджета build , именно там у меня есть доступ к BuildContext тому, что требуется для Provider

4. Да, это взорвется. Это одна из многих причин, по которой я перешел от поставщика к Riverpod (наследнику поставщика).

5. Я посмотрю на это. Хотя у меня есть рабочие примеры Provider , которые используются правильно. Возможно, я что-то упускаю