【问题标题】:Provider - Selector not updating UI for list items提供者 - 选择器不更新列表项的 UI
【发布时间】:2026-02-15 21:25:01
【问题描述】:

我有一个ListView 由几个ListTiles 组成,它们有一个尾随图标。图标的颜色应根据用户点击从透明变为绿色。但是,用户界面不会在用户交互时更新。

ServiceModel是这样的。

class ProviderService extends ChangeNotifier {
  final List<String> totalNames = ['Somesh', 'Tarulata', 'Indranil', 'Satyajyoti', 'Biswas', 'Sajal', 'Kumar', 'Slipa', 'Sonam', 'Neelam'];

  List<String> _selectedNames = [];
  List<String> get selectedNames => _selectedNames;

  void updateselectedNames(String name) {
    bool isExists = _selectedNames.contains(name);

    if (isExists)
      _selectedNames.remove(name);
    else
      _selectedNames.add(name);

    notifyListeners();
  }
}

ListView 是这样的。

class Members extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    ProviderService plService = Provider.of<ProviderService>(context, listen: false);

    return Scaffold(
      body: SafeArea(
        child: Selector<ProviderService, List<String>>(
          selector: (_, service) => service.selectedNames,
          builder: (context, selNames, child) {
            if (plService.totalNames.isEmpty) return child;

            return ListView.separated(
              shrinkWrap: true,
              itemBuilder: (context, index) {
                String _name = plService.totalNames[index];

                return ListTile(
                  title: Text('$_name'),
                  trailing: Icon(
                    Icons.check_circle,
                    color: selNames.contains(_name) ? Colors.lightGreen : Colors.transparent,
                  ),
                  onTap: () {
                    plService.updateselectedNames(_name),
                    print(selNames);
                  },
                );
              },
              separatorBuilder: (_, __) => Divider(),
              itemCount: plService.totalNames.length,
            );
          },
          child: Center(
            child: Text('No names have been found', textAlign: TextAlign.center),
          ),
        ),
      ),
    );
  }
}

当然main.dart是这样的。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ChangeNotifierProvider(
        create: (context) => ProviderService(),
        child: Members(),
      ),
    );
  }
}

即使列表selectedNames 已更新,用户界面仍保持不变。这里出了什么问题?

【问题讨论】:

    标签: flutter provider flutter-provider


    【解决方案1】:

    你可以添加Selector的shouldRebuild参数并返回true。像这样:

    Selector<ProviderService, List<String>>(
              selector: (_, service) => service.selectedNames,
              builder: (context, selNames, child) {...},
              shouldRebuild: (previous, next) => true,
           )
    

    【讨论】:

      【解决方案2】:

      当您使用Selector 时,您必须确保所选对象是不可变的。

      Selector<ProviderService, List<String>>(
        selector: (_, service) => service.selectedNames,
        builder: (context, selNames, child) { ...},
      ),
      

      builder 只会被调用一次,因为您的 selectedNames 对象始终保持不变。您正在同一数组对象中删除和添加项目。

      因此,您应该在 updateselectedNames 中提供一个新数组:

        void updateselectedNames(String name) {
          _selectedNames = _selectedNames.contains(name)
              ? _selectedNames.where((item) => item != name).toList()
              : [..._selectedNames, name];
          notifyListeners();
        }
      

      【讨论】:

        【解决方案3】:

        对于您的场景,我的方式是这样的。

           class ProviderService extends ChangeNotifier {
              final List<Name> totalNames = [
                 Name(name: 'Somesh', isTransparent: false),
                 Name(name: 'Tarulata', isTransparent: false),
              ];
            
              List<Name> _selectedNames = [];
              List<Name> get selectedNames => _selectedNames;
            
              void updateselectedNames(int index) {
                var exist = _isExist(totalNames[index]);
                if(exist){
                  _selectedNames.remove(totalNames[index]);
                } else {
                  _selectedNames.add(totalNames[index]);
                }
                totalNames[index].isTransparent = !totalNames[index].isTransparent;
                notifyListeners();
              }
        
              bool _isExist(Name name) {
                 var filter = _selectedNames.singleWhere(
                  (element) => element.name == name.name,
                  orElse: () => null,
                 );
                 return filter != null;
               }
            
            }
        
            class Name {
              String name;
              bool isTransparent;
              Name({this.name, this.isTransparent});
            }
        

        您可以在ListView 中为每个ListTile 使用Selector

        Selector<ProviderService, Name>(
          selector: (_, service) => service.totalNames[index],
          builder: (context, name, child) {
            return ListTile(
              title: Text('${name.name}'),
              trailing: Icon(
              Icons.check_circle,
              color: !name.isTransparent ? Colors.lightGreen : Colors.transparent,
            ),
            onTap: () {
              plService.updateselectedNames(index),
            },
        );
        

        【讨论】:

          最近更新 更多