0

我正在用颤振和提供者模式构建一个应用程序。我有一个特定的 ViewModel,它提供了Provider.of<AddressBookModel>(context).

class HomeScreen extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return ChangeNotifierProvider<AddressBookViewModel>(
          builder:(_) => AddressBookViewModel(),
          child: Scaffold(
              body: _getBody(context);
    }

    Widget _getBody(BuildContext context) {
        AddressBookViewModel vm = Provider.of<AddressBookViewModel>(context);

        // AddressBookViewModel holds a list of contact objects 
        // (id, name, street, starred etc.)
        List<Contact> contacts = vm.contacts; 
        return ListView.builder(
              itemCount: contacts.length,
              itemBuilder: (context, index) => ListTile(
                    title: Text(contacts[index].name),
                    trailing: contacts[index].starred
                        ? Icon(Icons.star))
                        : null,
                        /**
                         * Changing one object rebuilds and redraws the whole list
                         */
                        onLongPress: () => vm.toggleStarred(contacts[index]);
          ));
    }
}

以及各自的 ViewModel

class AddressBookViewModel with ChangeNotifier {
    final List<Contact> contacts;

    AddressBookViewModel({this.contacts = []});


    void toggleStarred(Contact contact) {
        int index = contacts.indexOf(contact);
        // the contact object is immutable
        contacts[index] = contact.copy(starred: !contact.starred);
        notifyListeners();
    }
}

我面临的问题是,一旦我用 更改列表中的一个联系人对象toggleStarred(),提供者就会重建和重绘整个列表。我认为这不是必需的,因为只需要重建一个条目。有没有办法让提供者只负责一个列表项?或者有什么其他方法可以解决这个问题?

4

2 回答 2

1

注意:最后提供完整代码

gistshowng 显示使用日志的应用程序

第 1 步:使用 ChangeNotifier 类扩展 Contact 类

class Contact with ChangeNotifier {  }

第 2 步:删除最终表格星标字段

  bool starred;

第 3 步:将 toggleStarred 方法从 AddressBookViewModel 类移动到 Contact 类

  void toggleStarred() {
    starred = !starred;
    notifyListeners();
  }

步骤[1,2,3] 代码更改审查:
class Contact with ChangeNotifier {
  final String name;
  bool starred;
  Contact(this.name, this.starred);

  void toggleStarred() {
    starred = !starred;
    notifyListeners();
  }
}

第 4 步:将 ListTile 移动到名为 ContactView 的 sprate StatelessWidget

class ContactView extends StatelessWidget {
   Widget build(BuildContext context) {
    return ListTile();
  }
}

第 5 步:更改 ListView itemBuilder 方法

(context, index) {
return ChangeNotifierProvider.value(
  value: contacts[index],
  child: ContactView(),
);

第 6 步:在新的 StatelessWidget ContactView 上使用 Provider 获取联系人

final contact = Provider.of<Contact>(context);

第 7 步:更改 onLongPress 以使用新的 toggleStarred

onLongPress: () => contact.toggleStarred(),

步骤[4,6,7] 代码更改审查:
class ContactView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final contact = Provider.of<Contact>(context);
    print("building ListTile item with contact " + contact.name);
    return ListTile(
      title: Text(contact.name),
      trailing: contact.starred ? Icon(Icons.star) : null,
      onLongPress: () => contact.toggleStarred(),
    );
  }
}
步骤[5] 代码更改审查:
return ListView.builder(
  itemCount: contacts.length,
  itemBuilder: (context, index) {
    print("building ListView item with index $index");
    return ChangeNotifierProvider.value(
      value: contacts[index],
      child: ContactView(),
    );
  },
);

完整代码

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

void main() {
  runApp(
    ChangeNotifierProvider<AddressBookViewModel>(
      builder: (context) => AddressBookViewModel(),
      child: HomeScreen(),
    ),
  );
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<AddressBookViewModel>(
      builder: (context) => AddressBookViewModel(),
      child: MaterialApp(
        home: Scaffold(
          body: _getBody(context),
        ),
      ),
    );
  }

  Widget _getBody(BuildContext context) {
    AddressBookViewModel vm = Provider.of<AddressBookViewModel>(context);

    final contacts = vm.contacts;
    return ListView.builder(
      itemCount: contacts.length,
      itemBuilder: (context, index) {
        print("building ListView item with index $index");
        return ChangeNotifierProvider.value(
          value: contacts[index],
          child: ContactView(),
        );
      },
    );
  }
}

// product_item.dart
class ContactView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final contact = Provider.of<Contact>(context);
    print("building ListTile item with contact " + contact.name);
    return ListTile(
      title: Text(contact.name),
      trailing: contact.starred ? Icon(Icons.star) : null,
      onLongPress: () => contact.toggleStarred(),
    );
  }
}

class AddressBookViewModel with ChangeNotifier {
  final contacts = [
    Contact("Contact A", false),
    Contact("Contact B", false),
    Contact("Contact C", false),
    Contact("Contact D", false),
  ];
  void addcontacts(Contact contact) {
    contacts.add(contact);
    notifyListeners();
  }
}

class Contact with ChangeNotifier {
  final String name;
  bool starred;
  Contact(this.name, this.starred);

  void toggleStarred() {
    starred = !starred;
    notifyListeners();
  }
}

参考:

于 2019-07-21T12:04:47.757 回答
1

使用列表时,您需要为列表中的每个项目提供一个“提供者”,并将列表项目提取到一个常量中——尤其是如果与您的项目关联的数据是不可变的。

代替:

final contactController = Provider.of<ContactController>(context);
return ListView.builder(
  itemCount: contactController.contacts.length,
  builder: (_, index) {
    reurn Text(contactController.contacts[index].name);
  }
)

更喜欢:

final contactController = Provider.of<ContactController>(context);
return ListView.builder(
  itemCount: contactController.contacts.length,
  builder: (_, index) {
    reurn Provider.value(
      value: contactController.contacts[index],
      child: const ContactItem(),
    );
  }
)

通常看起来像这样ContactItem的 a在哪里:StatelessWidget

class ContactItem extends StatelessWidget {
  const ContactItem({Key key}): super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(Provider.of<Contact>(context).name);
  }
}
于 2019-07-23T21:50:53.700 回答