Home > Software engineering >  Infinite-scroll listview.builder - to expand or not to expand... and more provider value not updatin
Infinite-scroll listview.builder - to expand or not to expand... and more provider value not updatin

Time:06-10

I am trying to build a view/route that will list items fetched from a REST source. I want to show a notification item below the list while the data is being fetched. But my ListView builder is constructed around the fetched data's structure, so I figured just have a ListTile fit some appropriate UX elements below the generated list inside a Column - which was kinda working great - or so I thought - until the list grows to fill the screen causing RenderFlex overflowed error. Wrapping the ListView builder in Expanded fixed that but moved the indicator to the bottom of the screen.

In trying to fix it I seem to have broken more of the plumbing and the boolean variable that should control the idicator widget; isLoading: stockSet.isBusyLoading doesn't seem to update.

At the moment if I hardcode it as `` it does sit in the appropraite position but I am back with the RenderFlex overflow.

Once all of this is working I'll be wanting to automatically load items untill the screen is full - not sure where I'll be triggering that from yet.

class MyStockSet extends StatefulWidget {
  const MyStockSet({super.key});

  static const indexStr = 'stocks';
  static const labelStr = 'Properties';

  @override
  State<MyStockSet> createState() => _MyStockSetState();
}

class _MyStockSetState extends State<MyStockSet> {
  @override
  Widget build(BuildContext context) {
    const String imagePath = 'assets/images/${MyStockSet.indexStr}.png';
    var assetImage = const AssetImage(imagePath);
    //var stockSet = context.watch<StockSet>(); <- didn't work either
    var stockSet = Provider.of<StockSet>(context,listen: false);
    return Scaffold(
      appBar: AppBar(
        title: Row(
          children: [
            AscHero(
              assetImage: assetImage,
              tag: MyStockSet.indexStr,
              title: MyStockSet.labelStr,
              radius: 32,
            ),
            const SizedBox(width: 12),
            const Text(MyStockSet.labelStr),
          ],
        ),
        actions: [
          IconButton(
            onPressed: () {
              var stockSet = context.read<StockSet>();
              int newNr = stockSet.stocks.length   1;
              Stock tmpstock = Stock(
                  id: newNr,
                  title: 'test$newNr',
                  thumbUrl: 'url',
                  description: 'desc');
              stockSet.add(tmpstock);
            },
            icon: const Icon(Icons.add),
          ),
          IconButton(
            onPressed: () {
              developer.log('btn before isBusyLoading ${stockSet.isBusyLoading}');
              stockSet.fetch();
              developer.log('after btn isBusyLoading ${stockSet.isBusyLoading}');
            },
            icon: const Icon(Icons.handshake),
          ),
        ],
      ),
      body: Column(
        children: [
          Row(
            // these will be filters, order toggle etc.
            children: [
              ElevatedButton(
                  onPressed: () => developer.log('Btn pressed.'),
                  child: Text('Btn')),
            ],
          ),
          Expanded(
            child: Column(children: [
              _StockListView(),
              LoadingStockListItemNotif(
                isLoading: true,
              ),
            ]),
          ),
        ],
      ),
    );
  }
}

class _StockListView extends StatefulWidget {
  @override
  State<_StockListView> createState() => _StockListViewState();
}

class _StockListViewState extends State<_StockListView> {

    @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    developer.log('_StockListView didChangeDependencies()');

    // developer.log('scroll pos ${_scrollController.position}');
  }

  @override
  Widget build(BuildContext context) {
    var stockSet = context.watch<StockSet>();

    return ListView.builder(
      // controller: _scrollController,
      shrinkWrap: true,
      itemCount: stockSet.stocks.length,
      itemBuilder: (context, index) => InkWell(
        child: StockListItem(
          stock: stockSet.stocks[index],
        ),
        onTap: () => Navigator.pushNamed(
          context,
          '/stocks/stock',
          arguments: ScreenArguments(stockSet.stocks[index]),
        ),
      ),
    );
  }

  void _scrollListener() {
    developer.log('_scrollListener');
  }
}

and

class StockSet extends ChangeNotifier {
  final List<Stock> _stocks = [];
  late bool isBusyLoading = false;

  List<Stock> get stocks => _stocks;

  void add(Stock stock) {
    _stocks.add(stock);
    developer.log('added stock :${stock.title}');
    notifyListeners();
  }

  void remove(Stock stock) {
    _stocks.remove(stock);
    notifyListeners();
  }

  Future<void> fetch() async {
    developer.log('fetch() iL T');
    isBusyLoading = true;
    notifyListeners();
    Stock tmpStock = await _fetchAStock();
    developer.log('fetch() iL F');
    isBusyLoading = false;
    notifyListeners();
    add(tmpStock);
  }

  Future<Stock> _fetchAStock() async {
    developer.log('fetch stock ');
    final response = await http.get(
      Uri.https(
        //...
      ),
    );
    developer.log('response.statusCode:${response.statusCode}');
    if (response.statusCode == 200) {
      final Map<String, dynamic> map = json.decode(response.body);
      return Stock(
        id: map['id'] as int,
        title: map['title'] as String,
        description: map['description'] as String,
        thumbUrl: map['thumbUrl'] as String,
      );
    }
    throw Exception('error fetching stocks:');
  }
}

Apologies for the convoluted question.

CodePudding user response:

Add mainAxisSize : MainAxisSize.min for the column inside the expanded widget. The expanded doesn't have any bounds and that's why it throws an error. You can wrap the column with a SingleChildScrollView if you have long content to display

CodePudding user response:

This worked for me!

Just set the shrinkWrap attribute to true

  • Related