0

I am working on a simple ListView app that gets populated from the internet and storing in the database using Provider. But the ListView doesn't get populated. It was getting null on items getter. But after I changed with Consumer.

Now I got an error, The getter items was called on null.

stackoverflow

 ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (15505): The following NoSuchMethodError was thrown building FutureBuilder<void>(dirty, state:
I/flutter (15505): _FutureBuilderState<void>#7948f):
I/flutter (15505): The getter 'items' was called on null.
I/flutter (15505): Receiver: null
I/flutter (15505): Tried calling: items
I/flutter (15505): 
I/flutter (15505): The relevant error-causing widget was:
I/flutter (15505):   FutureBuilder<void>
I/flutter (15505):   file:///C:/Users/Admin/lib/main.dart:27:13
I/flutter (15505): 
I/flutter (15505): When the exception was thrown, this was the stack:
I/flutter (15505): #0      Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
I/flutter (15505): #1      _MyAppState.build.<anonymous closure> (package:list_cart_interview/main.dart:37:33)
I/flutter (15505): #2      _FutureBuilderState.build (package:flutter/src/widgets/async.dart:751:55)
I/flutter (15505): #3      StatefulElement.build (package:flutter/src/widgets/framework.dart:4744:28)
I/flutter (15505): #4      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4627:15)
I/flutter (15505): #5      StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart

main.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'models/product.dart';
import 'provider/product_provider.dart';
import 'widgets/list_item.dart';

void main() {
  runApp(
    MaterialApp(
      home: ChangeNotifierProvider.value(
        value: ProductsProvider(),
        child: MyApp(),
      ),
    ),
  );
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder(
          future: Provider.of<ProductsProvider>(context, listen: false)
              .getProducts(),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return Center(
                child: CircularProgressIndicator(),
              );
            }

            print(snapshot.data.items.length);
            if (snapshot.connectionState == ConnectionState.done) {
              return Consumer<ProductsProvider>(
                child: Center(
                  child: CircularProgressIndicator(),
                ),
                builder: (context, productProvider, child) =>
                    productProvider.items.length <= 0
                        ? child
                        : ListView.builder(
                            itemCount: productProvider.items.length,
                            itemBuilder: (context, index) {
                              return ListItem();
                            },
                          ),
              );
            }
            return Container();
          }),
    );
  }
}

product_provider.dart

import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:list_cart_interview/hellper/sqlite_helper.dart';
import 'dart:convert';
import '../models/product.dart';

class ProductsProvider with ChangeNotifier {
  List<Products> _items = [];

  List get items {
    return [..._items];
  }

  Future<void> getDataFromInternet() async {
    try {
      Response response = await get('https://jsonkeeper.com/b/YIDG');

      Map<String, dynamic> extractedData = jsonDecode(response.body);

      final length = extractedData['data']['products'].length;
      print(length);

      for (int i = 0; i < length; i++) {
        DatabaseHelper.insert({
          'id': extractedData['data']['products'][i]['prodCode'],
          'prodName': extractedData['data']['products'][i]['prodName'],
          'prodImage': extractedData['data']['products'][i]['prodImage'],
          'prodPrice': extractedData['data']['products'][i]['prodPrice'],
          'cartQuantity': 0,
        });
      }
    } catch (error) {
      throw (error);
    }
  }

  Future getProducts() async {
    await getDataFromInternet();
    final productsList = await DatabaseHelper.getProductsFromDB();
    print(" hbj ${productsList}");

    _items = productsList
        .map(
          (item) => Products(
            item['id'],
            item['prodName'],
            item['prodImage'],
            item['prodPrice'],
            item['cartQuantity'],
          ),
        )
        .toList();
    print('-items: ${_items}');
    notifyListeners();
  }
}

I think getProducts() method is making the problem in above code. Now getting an only child is this widget. Please help

c49
  • 360
  • 1
  • 7
  • 18

2 Answers2

1

You need to check whether your snapshot.data has data or not before accessing the items.

if(snapshot.hasData) {
  // return your ListView
}

Miguel Ruivo
  • 16,035
  • 7
  • 57
  • 87
0

Try to avoid using Providers with FutureBuilder.
FutureBuilders depend on a Future i.e. a value is eventually returned.
In your case the provider calls notifyListeners(); and ends at that.
There is nothing being returned.
If you wish to use the items list, use a consumer which listens for changes.

If using a FutureBuilder is absolutely necessary, have your getProducts() method return some value which the FutureBuilder can use.
For example, you could return a boolean value based on whether you are getting data or not.