Search code examples
flutterdartflutter-provider

why does context.select only update the UI when ListView.builder is included in the column


When ListView.builder was removed, context.select doesn't update the Ui. Wondering why that's the case.

import 'package:flutter/material.dart';
import 'dart:math';
import 'package:provider/provider.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (context) => ListProvider(),
        ),
        ChangeNotifierProxyProvider<ListProvider, ListReceiver>(
          create: (context) => ListReceiver(),
          update: (_, listProvider, listReceiver) =>
              listReceiver..displayList(listProvider.firstList),
        )
      ],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        initialRoute: '/',
        routes: {
          '/': (context) => FirstPage(),
        },
      ),
    );
  }
}

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('This is a ProxyProvider test'),
      ),
      body: Builder(
        builder: (context) => Column(
          children: [
            ElevatedButton(
              onPressed: () {
                context.read<ListProvider>().addToList();
              },
              child: Text('add to list'),
            ),
            ListTile(
              title: Consumer<ListReceiver>(
                builder: (_, a, child) => Text(a.secondList.toString()),
              ),
              subtitle: Text(
                context.select((ListReceiver l) => l.secondList).toString(),
              ),
            ),
            SizedBox(
              height: 300,
              child: ListView.builder(
                itemCount:
                    context.select((ListReceiver d) => d.secondList.length),
                itemBuilder: (context, index) {
                  return Builder(
                      builder: (context) => ListTile(
                            title: Text(context.select((ListReceiver d) =>
                                d.secondList[index].toString())),
                            subtitle: Text('hi'),
                          ));
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class ListProvider extends ChangeNotifier {
  List firstList = [];
  var random = Random();

  void addToList() {
    firstList.add(random.nextInt(100));
    notifyListeners();
  }
}

class ListReceiver extends ChangeNotifier {
  List secondList;
  void displayList(List firstList) {
    secondList = firstList;
    notifyListeners();
    print(secondList);
  }
}

Solution

  • I cannot explain your observer behaviour :(

    For what it is worth, replacing:

            ListTile(
              title: Consumer<ListReceiver>(
                builder: (_, a, child) => Text(a.secondList.toString()),
              ),
              subtitle: Text(
                context.select((ListReceiver l) => l.secondList).toString(),
              ),
            ),
    

    with:

            ListTile(
              title: Text(context.watch<ListReceiver>().secondList.toString()),
              subtitle: Text(
                context.select((ListReceiver l) => l.secondList).toString(),
              ),
            ),
    

    does work.