Search code examples
flutterstream-builder

StreamBuilder / Clicking through to another page


I have an app that has 2 pages. The first page lists all records. If a user clicks on an item in the list the user is then taken to the second page, that should display the record's details. The app compiles without error but in the Simulator if I try clicking on one of the links on the 1st page then I get the following error:

NoSuchMethodError: The getter 'data' was called on null.
Receiver: null
Tried calling: data

Please see the code below (I tried putting the StreamBuilder high up the hierarchy so that it's not possible to get a null error).

Please can someone point me in the right direction? Thanks

void main() async {
    WidgetsFlutterBinding.ensureInitialized();
    await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

    runApp(Home());
}
    
class Home extends StatelessWidget {
    // const Home({Key? key}) : super(key: key);
    
    @override
    Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
    
          routes: {
            '/': (context) => ItemList(),
            '/factfile': (context) => ItemDetails(),
          },
        );
    }
}
    
class ItemList extends StatelessWidget {
    ItemList({Key? key}) : super(key: key) {
        _stream = _reference.snapshots();
    }
    
    CollectionReference _reference = FirebaseFirestore.instance.collection('table_name');
    
    late Stream<QuerySnapshot> _stream;
    
    @override
    Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Item List'),
          ),
          body: StreamBuilder<QuerySnapshot>(
            stream: _stream,
            builder: (BuildContext context, AsyncSnapshot snapshot) {
              //Check error
              if (snapshot.hasError) {
                return Center(child: Text('Some error occurred ${snapshot.error}'));
              }
    
              //Check if data arrived
              if (snapshot.hasData) {
                //get the data
                QuerySnapshot querySnapshot = snapshot.data;
                List<QueryDocumentSnapshot> documents = querySnapshot.docs;
    
                //Convert the documents to Maps
                List<Map> items = documents.map((e) =>
                {
                  'name': e['name'],
                  'dlastupd': e['dlastupd'],
                  'dlastupd date': DateTime.fromMillisecondsSinceEpoch(e['dlastupd'] * 1000),
                  'id': e['id'],
                }).toList();
    
                // sort in descending date order
                items.sort((a, b) => b["dlastupd"].compareTo(a["dlastupd"]));
    
                //Display the list
                return ListView.builder(
    
                    itemCount: items.length,
                    itemBuilder: (BuildContext context, int index) {
                      //Get the item at this index
                      Map thisItem = items[index];
                      //Return the widget for the list items
                      return ListTile(
    
                        title: Text(formatDate(thisItem['dlastupd date'], [dd, '', M]) + " - " + thisItem['name']),
                        onTap: () {
                          Navigator.pushNamed(
                            context,
                            '/factfile',
                            arguments: {
                              'id': thisItem['id'],
                            },
                          );
                        },
                      );
                    });
              }
    
              //Show loader
              return Center(child: CircularProgressIndicator());
            },
          ), //Display a list // Add a FutureBuilder
        );
    }
}
    
class ItemDetails extends StatelessWidget {
    // final String itemId;
    
    Map routeData = {};
    
    // ItemDetails(this.itemId, {Key? key}) : super(key: key);
    
    @override
    Widget build(BuildContext context) {
        routeData = ModalRoute
            .of(context)
            ?.settings
            .arguments as Map;
        print(routeData);
    
        final DocumentReference docRef =
        FirebaseFirestore.instance.collection(“table_name”).doc(routeData['id']);
    
        var snapshot;
        final data = snapshot.data!.data() as Map<String, dynamic>;
        
        return FutureBuilder<DocumentSnapshot>(
          future: docRef.get(),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return Scaffold(
                appBar: AppBar(
                  title: Text("Waiting for future..."),
                ),
                body: Center(child: CircularProgressIndicator()),
              );
            } else if (snapshot.hasError) {
              return Scaffold(
                appBar: AppBar(
                  title: Text("Error"),
                ),
                body: Center(child: Text("Error: ${snapshot.error}")),
              );
            } else {
              return Scaffold(
                appBar: AppBar(
                  title: Text(data?["name"]),
                ),
                body: Center(child: Text("Data has been loaded")),
              );
            }
          },
        );
    }
}

Solution

  • I took a quicklook, but it seems there's something missed:

    In your build method after extraction of your passed data. snapshot is declared but not initialized, it's null and you are trying to access data (the compiler warned you but you used exclamation operator to claim that snapshot is not null).

    var snapshot;
    final data = snapshot.data!.data() as Map<String, dynamic>;
    

    So, figure out the purpose of the snapshot and fix it.

    Hope it helps you.