-1

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

main.dart

void main() {
  
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: HomeScreen(title: 'Email Client App'),
    );
  }
}

home_screen.dart

class HomeScreen extends StatelessWidget {
  HomeScreen({this.title});
  final String title;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(title),
        ),
        body: MessagesScreen());
  }
}

message_screen.dart

import 'dart:convert';
import 'package:email_client_app/models/message.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class MessagesScreen extends StatefulWidget {
  @override
  _MessagesScreenState createState() => _MessagesScreenState();
}

class _MessagesScreenState extends State<MessagesScreen> {
  Future loadMessageFromAsset() async {
    await Future.delayed(Duration(seconds: 2));
    return await rootBundle.loadString("data/message.json");
  }

  Future<List<Message>> getMessagesFromAsset() async {
    String jsonString = await loadMessageFromAsset();
    List<dynamic> data = json.decode(jsonString);
    List<Message> messages =
        data.map((data) => Message.fromJson(data)).toList();
    return messages;
  }

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

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: getMessagesFromAsset(),
        builder: (context, messages) {
          List<Message> messageList = messages.data;
          return ListView.builder(
            itemCount: 5,
            itemBuilder: (context, index) {

** This is where the error is caught!**

              var message = messageList[index];

--

              return ListTile(
                isThreeLine: true,
                leading: CircleAvatar(child: Text('ND')),
                title: Text(messageList[index].title),
                subtitle: Text(
                  message.body,
                  maxLines: 2,
                ),
                trailing: Icon(
                  Icons.more_horiz_outlined,
                  color: Colors.blueAccent,
                ),
              );
            },
          );
        });
  }
}

message.dart (Message models class). # Retrieving local data-source from message.json

class Message {
  Message({this.title, this.body});
  String title, body;

  factory Message.fromJson(Map<String, dynamic> data) {
    return Message(title: data['title'], body: data['body']);
  }

  Map<String, dynamic> toJson() {
    return {'title': title, 'body': body};
  }
}

message.json

[
    {
        "title": "Title 1",
        "body": "Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consectetur adipisci elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua."
    },
    {
        "title": "Title 2",
        "body": "Lorem ipsum dolor sit amet. "
    }
]
RaSha
  • 1,356
  • 1
  • 16
  • 30
Nikash Deka
  • 427
  • 2
  • 4
  • 15
  • 1
    Also I wanted to move 'loadMessageFromAsset()' & 'getMessagesFromAsset()' into a different file under services. In that case, how do I grab its data in the message_screen.dart? – Nikash Deka Nov 17 '20 at 17:44

2 Answers2

1

Return indicator or anything when connection state is waiting.

return FutureBuilder(
        future: getMessagesFromAsset(),
        builder: (context, messages) {

          // add this one
          if (messages.connectionState == ConnectionState.waiting) {
                return Text("Loading...");
          }
          List<Message> messageList = messages.data;
          return ListView.builder(
            itemCount: 5,
            itemBuilder: (context, index) {
              return ListTile(
                isThreeLine: true,
                leading: CircleAvatar(child: Text('ND')),
                title: Text(messageList[index].title),
                subtitle: Text(
                  message.body,
                  maxLines: 2,
                ),
                trailing: Icon(
                  Icons.more_horiz_outlined,
                  color: Colors.blueAccent,
                ),
              );
            },
          );
        });
Rajitha Udayanga
  • 1,559
  • 11
  • 21
1

There are a couple of things that I think you are doing wrong.

  • You are calling the method getMessagesFromAsset at two places: initState & FutureBuilder. Remove from initState.

  • Your method getMessageskFromAsset might need to use a different variable name in the map method:

    Future<List<Message>> getMessagesFromAsset() async {
         String jsonString = await loadMessageFromAsset();
         List<dynamic> data = json.decode(jsonString);
         List<Message> messages =
             data.map((messageData) => Message.fromJson(messageData)).toList(); // here
         return messages;
       }
    
  • Check whether the messages list is null or not.

    List<Message> messageList = messages.data ?? [];
    
  • Inside ListView.builder, you are using a fixed value 5, which will lead to index out of bounds exception.

    return ListView.builder(
         itemCount: messages?.length ?? 0, // Use messages.length instead of `5`
    
Ravi Singh Lodhi
  • 2,605
  • 1
  • 9
  • 12
  • # itemCount is embarrassingly missed. # Checking the messageList for null value was an excellent suggestion. Just a clarification though, already a connectionState condition is working so why do we need to check if the list is empty? – Nikash Deka Nov 17 '20 at 17:59
  • Also I wanted to move 'loadMessageFromAsset()' & 'getMessagesFromAsset()' into a different file under services. In that case, how do I grab its data in the message_screen.dart? – Nikash Deka Nov 17 '20 at 18:03
  • I don't see any `connectionState` condition in the above code. Still, if the server responded with an empty list, you should be able to handle that condition also. That's why you should check if the list is empty & return the appropriate widget according to it. – Ravi Singh Lodhi Nov 18 '20 at 05:01
  • In order to get data in the `message_screen.dart` from other files, you should pass the data as arguments to the `MessageScreen` widget. – Ravi Singh Lodhi Nov 18 '20 at 05:02
  • I updated my code base with the connectionState with a CircularProgressiveIndicator widget. Also moved the functions for loading and getting the data to services. Got it running now. – Nikash Deka Nov 18 '20 at 15:54
  • That's really great to hear. If you found my answer helpful, you can upvote or mark the answer as selected. – Ravi Singh Lodhi Nov 18 '20 at 16:17