允许区分提供者

时间:2019-07-18 20:43:15

标签: flutter dart flutter-provider

我正在使用带有Flutter和提供程序模式的应用程序。我有一个特别的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()更改列表中的一个联系人对象, 提供者正在重建并重新绘制整个列表。我认为这是没有必要的,因为 一项需要重建。有什么办法让提供者只负责 一个清单项目?或其他解决此问题的方法?

2 个答案:

答案 0 :(得分: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(),
    );
  }
)

ContactItemStatelessWidget的地方,通常看起来像这样:

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

  @override
  Widget build(BuildContext context) {
    return Text(Provider.of<Contact>(context).name);
  }
}

答案 1 :(得分:0)

注意:完整代码可在最后获得

gistshowng showing the app working with the logs

步骤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的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上,使用提供程序获取联系人

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

步骤7:更改onLongPress以使用新的toggleStard

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();
  }
}

Ref: