Search code examples
firebasefluttergoogle-cloud-firestoreflutter-provider

Flutter Provider showing results ONLY after Hot Reload


I am a beginner, and I am building an app following a tutorial using provider. The logic is that, when You click the "Categories" widget, it should lead you to a page with products in that category. The logic works as expected except I have to first refresh the code for results to show. I frankly do not know what I am doing wrong.

From the Homepage, the Categories widget is rendered using a listview builder

Container(
                height: 75,
                child: ListView.builder(
                  scrollDirection: Axis.horizontal,
                  itemCount: categoryProvider.categories.length,
                  itemBuilder: (context, index) {
                    return GestureDetector(
                      onTap: () async {
                        await productProvider.loadProductsByCategory(
                            categoryName:
                                categoryProvider.categories[index].name);
                        changeScreen(
                            context,
                            CategoryScreen(
                                categoryModel:
                                    categoryProvider.categories[index]));
                      },
                      child: CategoriesWidget(
                          category: categoryProvider.categories[index]),
                    );
                  },
                ),
              )

Main.Dart

I am listening to providers like this

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MultiProvider(
      providers: [
        ChangeNotifierProvider.value(value: AppProvider()),
        ChangeNotifierProvider.value(value: UserProvider.initialize()),
        ChangeNotifierProvider.value(value: CategoryProvider.initialize()),
        ChangeNotifierProvider.value(value: MarketProvider.initialize()),
        ChangeNotifierProvider.value(value: ProductProvider.initialize()),
      ],
      child: MaterialApp(
          debugShowCheckedModeBanner: false,
          title: 'Akatale',
          theme: ThemeData(
            primaryColor: Colors.black,
          ),
          home: ScreenController())));
}

class ScreenController extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final auth = Provider.of<UserProvider>(context);

    switch (auth.status) {
      case Status.Uninitialized:
        return Loading();
      case Status.UnAuthenticated:
      case Status.Authenticating:
        return SignInScreen();
      case Status.Authenticated:
        return MainScreen();
      default:
        return SignInScreen();
    }
  }
}

Product Service

With this, I fetch products from firestore by the name of the category and add them to a List

class ProductService extends ChangeNotifier {
  String collection = "products";
  Firestore _firestore = Firestore.instance;


  Future<List<ProductModel>> getProductsByCategory({String category}) async {
    List<ProductModel> products = [];
    _firestore
        .collection(collection)
        .where("category", isEqualTo: category)
        .getDocuments()
        .then((result) {
      for (DocumentSnapshot product in result.documents) {
        products.add(ProductModel.fromSnapshot(product));
      }
    });
    return products;
  }
}

Product Provider

Here I have public methods to be called in widgets

class ProductProvider with ChangeNotifier {
  ProductService _productService = ProductService();
  List<ProductModel> products = [];
  List<ProductModel> productsByCategory = [];


  ProductProvider.initialize() {
    _loadProducts();
   
  }

  //Private method Load products to List
  Future _loadProducts() async {
    products = await _productService.getProducts();
    notifyListeners();
  }

  //public method to Load products to List by category
  Future loadProductsByCategory({String categoryName}) async {

    productsByCategory =
        await _productService.getProductsByCategory(category: categoryName);

    notifyListeners();
  }
}

Categories Page

This is where products are rendered based on the chosen category

class CategoryScreen extends StatelessWidget {
  final CategoryModel categoryModel;

  const CategoryScreen({Key key, this.categoryModel}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    final productProvider = Provider.of<ProductProvider>(context);
    return Scaffold(
      body: SafeArea(
          child: ListView(
        children: [
          Text(categoryModel.name),
          Column(
            children: productProvider.productsByCategory.map((item) {
              return GestureDetector(
                  onTap: () {
                    //Load details page
                  },
                  child: ProductWidget(
                    product: item,
                  ));
            }).toList(),
          ),
        ],
      )),
    );
  }
}

A blank page is displayed Categories page

But on hitting Hot reload, the products appear enter image description here


Solution

  • In getProductsByCategory in your ProductsService you are not awaiting getDocuments. So while the documents will get loaded it already calls notifyListeners in the model before it is actually done loading them. And thus when you rebuild using hotreload they show up

    To fix this simply await the getDocuments in the service