skip to Main Content

I have a question about this error that I have in my code. So, the app should show a list of products read from firebase. This is the code placed in the screen where I want to show Products:

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'productWidget.dart';
import 'product.dart';

class ColinaMenu extends StatefulWidget {
  const ColinaMenu({Key? key}) : super(key: key);

  @override
  State<ColinaMenu> createState() => _ColinaMenuState();
}

class _ColinaMenuState extends State<ColinaMenu> {
  Future<List<String>> getDocId() async {
    final snapshot = await FirebaseFirestore.instance.collection('produse').get();
    final docIDs = snapshot.docs.map((doc) => doc.id).toList();
    print('Number of products found: ${docIDs.length}');
    print('Product IDs found: $docIDs');
    return docIDs;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Meniu Colina'),
      ),
      body: SafeArea(
        child: FutureBuilder<List<String>>(
          future: getDocId(),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return const Center(
                child: CircularProgressIndicator(),
              );
            } else if (snapshot.hasError) {
              return Text('Error: ${snapshot.error}');
            } else {
              final docIDs = snapshot.data!;
              return Container(
                height: MediaQuery.of(context).size.height,
                child: ListView.builder(
                  itemCount: docIDs.length,
                  itemBuilder: (BuildContext context, int index) {
                    final product = GetProducts(docIDs[index]).product;
                    print('Product name: ${product.name}');
                    return widgetProduct(product: product);
                  },
                ),
              );
            }
          },
        ),
      ),
    );
  }
}

Next we have the code for Product and getProducts classes:

import 'package:cloud_firestore/cloud_firestore.dart';

class Product {
  late String name;
  late String price;
  late bool active;
  late String id;

  Product({
    required this.name,
    required this.price,
    required this.active, required String id,
  });
}

class GetProducts {
  final String docID;
  late Product product;

  GetProducts(this.docID) {
    FirebaseFirestore.instance.collection('produse').doc(docID).snapshots().listen((snapshot) {
      product = Product(
        id: snapshot.id,
        name: snapshot['name'],
        price: snapshot['price'],
        active: snapshot['active'],
      );
    });
  }
}

And finally the code for widgetProduct class where I designed how a product should be showed in the screen:

import 'package:flutter/material.dart';
import 'package:udelivery/product.dart';
import 'theme_data.dart';


class widgetProduct extends StatefulWidget {
  Product product;
  widgetProduct({
    Key? key,
    required this.product
  }) : super(key: key);

  @override
  State<widgetProduct> createState() => _widgetProductState();
}

class _widgetProductState extends State<widgetProduct> {
  var qty = ['1', '2', '3', '4'];
  late String _selectedValue=qty[1];

  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.fromLTRB(0, 15, 0, 0),
      padding: const EdgeInsets.fromLTRB(10, 5, 5, 5),
      decoration: BoxDecoration(
          color: Theme.of(context).scaffoldBackgroundColor,
          borderRadius: BorderRadius.circular(10),
          boxShadow: const [
            BoxShadow(
              spreadRadius: 1,
              blurRadius: 5,
              offset: Offset(0, 1), // changes position of shadow
            ),
          ]),
      child: Column(
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              Expanded(
                flex: 6,
                child: Text(
                  widget.product.name,
                  style: TextStyle(
                    color: MyTheme.textColor(context),
                  ),
                ),
              ),
              Expanded(
                flex: 1,
                child:  DropdownButton(
                  value: _selectedValue,
                  items: qty.map(
                          (e){
                            return DropdownMenuItem(value: e, child: Text(e, style: TextStyle(color: MyTheme.textColor(context)),),);
                          }
                  ).toList(),
                  onChanged: (val){
                    setState(() {
                      _selectedValue = val as String;
                    });
                  },
                )
              ),
              Expanded(
                  flex: 1,
                  child: IconButton(
                          onPressed: () {},
                          icon: Icon(
                            Icons.add_circle_rounded,
                            color: MyTheme.buttonColor(context),
                          ))),
            ],
          ),
          Row(children: [
            Text(
              '${widget.product.price} lei',
              style: TextStyle(
                color: MyTheme.buttonColor(context),
              ),
            ),
          ]),
        ],
      ),
    );
  }
}

When I run the app, after a few secconds when CircularProgresIndicator is loading, I recieve this error: "LateInitializationError: Field ‘product’ has not been initialized.". How can I fix that error?

3

Answers


  1. Currently, you have declared product as late and it is only assigned a value inside the listen callback of the stream. This means that it might not have been initialized by the time you try to use it.

    You can remove the late keyword and initialize the product variable to a default value.

    Or, you can use a completer to return the value once it’s ready:

    class GetProducts {
      final String docID;
    
      GetProducts(this.docID);
    
      Future<Product> getProduct() async {
        final completer = Completer<Product>();
        final docSnapshot = await FirebaseFirestore.instance.collection('produse').doc(docID).get();
    
        if (docSnapshot.exists) {
          final product = Product(
            id: docSnapshot.id,
            name: docSnapshot['name'],
            price: docSnapshot['price'],
            active: docSnapshot['active'],
          );
          completer.complete(product);
        } else {
          completer.completeError('Product not found');
        }
    
        return completer.future;
      }
    }
    

    Use it like this:

    itemBuilder: (BuildContext context, int index) async {
      final product = await GetProducts(docIDs[index]).getProduct();
      print('Product name: ${product.name}');
      return widgetProduct(product: product);
    },
    
    Login or Signup to reply.
  2. You are using late keyword and assigning the value at the same time.

    Try assigning the value in initState

    var qty = ['1', '2', '3', '4'];
    late String _selectedValue;
    
    @override
    initState(){
      _selectedValue=qty[1]
    } 
    
    Login or Signup to reply.
  3. Instead of declaring Product like this:

    class Product {
      late String name;
      late String price;
      late bool active;
      late String id;
    
      Product({
        required this.name,
        required this.price,
        required this.active, required String id,
      });
    }
    class GetProducts {
      final String docID;
      late Product product;
    

    declare it like this:

    class Product {
     final String name;
     final String price;
     final bool active;
     final String id;
    
      Product({
        required this.name,
        required this.price,
        required this.active, 
        required String id,
      });
    }
    class GetProducts {
      final String docID;
      Product? product;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search