0
votes

I am running my app and getting the error:

"NoSuchMethodError: The Method '[ ]' was called on null. Receiver: null. Tried calling: ."

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';

import '../model/Lesson.dart';


class MobileModeScreen extends StatefulWidget {

  @override
  _MobileModeScreenState createState() => _MobileModeScreenState();
}


class _MobileModeScreenState extends State<MobileModeScreen> {

  List<Lesson> _lesson;


  @override
  void initState() {
    super.initState();
    Future.delayed(Duration.zero,() {
      getLessonFromJSON(context);
    });
    print("lessonDescription: ${_lesson[0].lessonTitle}");
  }

  Future<List<Lesson>> getLessonFromJSON(BuildContext context) async {
    String jsonString = await DefaultAssetBundle.of(context).loadString("assets/lessons/lessons.json");

    return await Future.delayed(Duration.zero,() {
      List<dynamic> data = jsonDecode(jsonString);
      List<Lesson> lesson = data.map( (f) => Lesson.fromJSON(f) ).toList();
      _lesson = lesson;
      print("lessonDescription: ${_lesson[0].lessonTitle}");
      return _lesson;
    });
  }


  @override
  Widget build(BuildContext context) {

    return Directionality(
      textDirection: TextDirection.rtl,
      child: Scaffold(
        appBar: AppBar(
          title: Container(
            padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
            decoration: BoxDecoration(
              color: Color(0xffFFFFFF),
              border: Border.all(
                color: Color(0xffF5ED02),
                width: 3,
              ),
              borderRadius: BorderRadius.circular(25.0),
            ),
            child: Text("lessonDescription: ${_lesson[0].lessonTitle}",
              style: TextStyle(
                //fontFamily: "Uthmani",
                color: Color(0xff225587),
              ),
            ),
          ),
          centerTitle: true,
          backgroundColor: Color(0xff7F3F96),
        ),

        body: ListView(
          padding: const EdgeInsets.fromLTRB(5, 15, 5, 5),
          children: <Widget>[
          ],
        ),
        backgroundColor: Color(0xffF0E4F2),
      ),
    );
  }
}

I'm not getting the error when I try to access the value from the getLessonFromJSON function, but outside of the function, it's not working at all.

The error I get:

════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building Builder:
The method '[]' was called on null.
Receiver: null
Tried calling: [](0)

The relevant error-causing widget was: 
  MaterialApp file:///home/hmalabeh/AndroidStudioProjects/Flutter/Bounyan/boynyan_final/lib/main.dart:12:12
When the exception was thrown, this was the stack: 
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1      _MobileModeScreenState.initState (package:boynyanfinal/screens/mobile_mode_screen.dart:26:40)
#2      StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4355:58)
#3      ComponentElement.mount (package:flutter/src/widgets/framework.dart:4201:5)
#4      Element.inflateWidget (package:flutter/src/widgets/framework.dart:3194:14)
...

Can someone help please.

Update: I want to be able to use what I get from the function getLessonFromJSON in the build method like I'm using in the Text filed in the AppBar

4

4 Answers

0
votes

_lesson is null because its value is assigned only when Future.delayed is resolved.

To solve it, either move print("lessonDescription: ${_lesson[0].lessonTitle}"); inside Future handler (after getLessonFromJSON(context);) or just remove that line at all because as far as I can suppose, it's needed only for debugging purposes.

This should work:

  void initState() {
    super.initState();
    Future.delayed(Duration.zero,() {
      getLessonFromJSON(context);
      print("lessonDescription: ${_lesson[0].lessonTitle}");
    });
  }
0
votes

Move the get data to a different function and await the response:

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

void load() async {
  setState(() {
    await getLessonFromJSON(context);
  });
}

Also place a loader anywhere you're using _lesson, example in your Appbar:

var text = _lesson == null ? 'Loading...' : 'lessonDescription: ${_lesson[0].lessonTitle}';
Text(text,
  style: TextStyle(
  //fontFamily: "Uthmani",
    color: Color(0xff225587),
  ),
)

0
votes

You should initialize '_lesson' with an empty list ``` List _lesson = [];```` And then add item to this list in initState by using _lesson.add(item) and called setState to update the state of app. This will work but you should FutureBuilder for async operations and use provider or bloc for better State management

0
votes

I've added the setState() inside the getLessonFromJSON and I've added a loader in the places I use values from _lesson as @Sami suggested.

_lesson doesn't need to ve initialized.

@override
  void initState() {
    super.initState();
    getLessonFromJSON(context);
  }

  Future<List<Lesson>> getLessonFromJSON(BuildContext context) async {
    String jsonString = await DefaultAssetBundle.of(context).loadString("assets/lessons/lessons.json");

    try{
      return await Future.delayed(Duration.zero,() {
        List<dynamic> data = jsonDecode(jsonString);
        List<Lesson> lesson = data.map( (f) => Lesson.fromJSON(f) ).toList();
        setState(() {
          _lesson = List.from(lesson);
        });
        print("[getLessonFromJSON] lessonDescription: ${lesson[0].lessonTitle}");
        return lesson;
      });
    } catch(e){
      print(e);
    }
  }

The loader

_lessonTitle = _lesson == null ? 'Loading...' : _lesson[0].lessonTitle;