草庐IT

dart - Flutter 区 block 模式 : Update BloC Streams Based Another BloC's Stream

coder 2023-07-22 原文

场景

我正在尝试创建一个具有两个屏幕的 Flutter 应用程序:ContactsScreenEditContactScreen .在 ContactsScreen , 用户将看到 DropdownButtonText . DropdownButton拥有 Contact 的列表通过 api 获取的对象。每当用户选择 Contact来自 DropdownButton , Text对象将显示有关该特定联系人的信息。此外,根据 Contact选择,一个RaisedButton将出现,单击后会将用户定向到 EditContactScreen编辑选定的 Contact .我正在使用 BloC 模式。我创建了两个 BloC,每个屏幕一个:ContactsScreenBlocEditContactScreenBloc . ContactsScreenBloc持有Stream<Contact>和一个 Sink<Contact>用于管理选定的 Contact .鉴于 EditContactScreenBlocContact 保留流和汇领域。最后,我有一个 GlobalBloc包含 Contact 的列表秒。 GlobalBloc是一个 InheritedWidget包裹了 MaterialApp .该应用程序过于简单,并且是更大应用程序的一部分,因此,我无法合并 ContactsScreenBlocEditContactScreenBloc , 并且应该有一个 GlobalBlocContact 的列表

问题

我实际上是 Flutter 的新手,所以我不确定我的方法是否合理。如果是,则当用户导航到 EditContactScreen 时并成功更新 Contact , 我怎样才能在选择的 Contact 中反射(reflect)出来呢?在ContactsScreen

代码片段

contact.dart

class Contact {
  final String id;
  final String firstName;
  final String lastName;
  final String phoneNumber;

  Contact({this.id, this.firstName, this.lastName, this.phoneNumber});
  Contact.fromJson(Map<String, dynamic> parsedJson)
        : id = parsedJson['id'],
          firstName = parsedJson['firstName'],
          lastName = parsedJson['lastName'],
          phoneNumber = parsedJson['phoneNumber'];

  copyWith({String firstName, String lastName, String phoneNumber}) => Contact(
    id: id,
    firstName: firstName ?? this.firstName,
    lastName: lastName ?? this.lastName,
    phoneNumber: phoneNumber ?? this.phoneNumber
  );

  @override
  bool operator ==(other) => other.id == this.id;

  @override
  int get hashCode => id.hashCode;
}

global.bloc.dart

class GlobalBloc {
  final _repo = Repo();

  final _contacts = BehaviorSubject<List<Contact>>(seedValue: []);

  Stream<List<Contact>> get contacts => _contacts.stream;

  Function(List<Contact>) get updateContacts => _contacts.sink.add;

  Future refreshContacts() async{
    final contacts = await _repo.getContacts();
    updateContacts(contacts);
  }
}

contacts_screen.bloc.dart

class ContactsScreenBloc {
  final _selectedContact = BehaviorSubject<Contact>(seedValue: null);

  Stream<Contact> get selectedContact => _selectedContact.stream;

  Function(Contact) get changeSelectedContact => _selectedContact.sink.add;
}

edit_contacts_screen.bloc.dart

class ContactsScreenBloc {
  final _selectedContact = BehaviorSubject<Contact>(seedValue: null);

  Stream<Contact> get selectedContact => _selectedContact.stream;

  Function(Contact) get changeSelectedContact => _selectedContact.sink.add;
}

global.provider.dart

class GlobalProvider extends InheritedWidget {
  final bloc = GlobalBloc();
  static GlobalBloc of(BuildContext context) => (context.inheritFromWidgetOfExactType(GlobalProvider) as GlobalProvider).bloc;
  bool updateShouldNotify(_) => true;
}

contacts.screen.dart

class ContactsScreen extends StatelessWidget {
  final bloc = ContactsScreenBloc();

  @override
  Widget build(BuildContext context) {
    final globalBloc = GlobalProvider.of(context);

    return Column(
      children: <Widget>[
        StreamBuilder<List<Contact>>(
          stream: globalBloc.contacts,
          builder: (context, listSnapshot) {
            return StreamBuilder<Contact>(
              stream: bloc.selectedContact,
              builder: (context, itemSnapshot) {
                return DropdownButton<Contact>(
                  items: listSnapshot.data
                      ?.map(
                        (contact) => DropdownMenuItem<Contact>(
                              value: contact,
                              child: Text(contact.firstName + ' ' + contact.lastName),
                            ),
                      )
                      ?.toList(),
                  onChanged: bloc.changeSelectedContact,
                  hint: Text('Choose a contact.'),
                  value: itemSnapshot.hasData ? itemSnapshot.data : null,
                );
              },
            );
          },
        ), // end for DropdownButton StreamBuilder

        StreamBuilder<Contact>(
          stream: bloc.selectedContact,
          builder: (context, snapshot) {
            return snapshot.hasData
                ? Row(children: <Widget>[
                Text(snapshot.data.firstName + ' ' + snapshot.data.lastName + ' ' + snapshot.data.phoneNumber),
                FlatButton(
                  child: Text('Edit Contact'),
                  onPressed: () {
                    Navigator.of(context).push(MaterialPageRoute(
                        builder: (context) =>
                            EditContactScreen(contact: snapshot.data)));
                  },
                ),
              ],
            )
                : null;
          }, // End for text description
        )
      ],
    );
  }
}

edit_contact.screen.dart

class EditContactScreen extends StatelessWidget {
  final bloc = EditContactScreenBloc();
  final Contact contact;

  EditContactScreen({this.contact});

  @override
  Widget build(BuildContext context) {
    final globalBloc = GlobalProvider.of(context);

    return Column(
      children: <Widget>[
        TextField(onChanged: (firstName) => bloc.updateContact(contact.copyWith(firstName: firstName))),
        TextField(onChanged: (lastName) => bloc.updateContact(contact.copyWith(firstName: lastName))),
        TextField(onChanged: (phoneNumber) => bloc.updateContact(contact.copyWith(firstName: phoneNumber))),
        RaisedButton(child: Text('Update'), onPressed: () async {
          await bloc.update();
          await globalBloc.refreshContacts();
          Navigator.of(context).pop();
        },)
      ],
    );
  }
}

最佳答案

好的,我能够解决我的问题:

contacts_screen.bloc.dart ,我添加了以下方法:

void updateContactInfo(List<Contact> contacts) {
    final contact = _selectedContact.value;
    if (contact == null) return;
    final updatedContact = contacts.firstWhere((a) => a.id == contact.id);
    if (updatedContact == null) return;
    changeSelectedContact(updatedContact);
  }

并更新了StreamBuilder<List<Contact>>用于构建 DropdownButton变成这样:

StreamBuilder<List<Contact>>(
  stream: globalBloc.contacts,
  builder: (context, listSnapshot) {
    bloc.updateContactInfo(listSnapshot.data);
    return StreamBuilder<Contact>(
      stream: bloc.selectedContact,
      builder: (context, itemSnapshot) {
        return DropdownButton<Contact>(
          items: listSnapshot.data
              ?.map(
                (contact) => DropdownMenuItem<Contact>(
                      value: contact,
                      child: Text(
                          contact.firstName + ' ' + contact.lastName),
                    ),
              )
              ?.toList(),
          onChanged: bloc.changeSelectedContact,
          hint: Text('Choose a contact.'),
          value: itemSnapshot.hasData ? itemSnapshot.data : null,
        );
      },
    );
  },
)

关于dart - Flutter 区 block 模式 : Update BloC Streams Based Another BloC's Stream,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53918475/

有关dart - Flutter 区 block 模式 : Update BloC Streams Based Another BloC's Stream的更多相关文章

  1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  6. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  7. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  8. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  9. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  10. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

    我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

随机推荐