[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is. \nShow the code you wrote as completely as possible.\n```dart\n/// your code here\n``` \n\n**To Reproduce**\nSteps to reproduce the behavior.\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Additional context**\n1. The version of fish-redux which you are using.\n2. The information from flutter doctor.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/custom.md",
    "content": "---\nname: Custom issue template\nabout: Describe this issue template's purpose here.\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n.atom/\n.idea/\n.vscode/\n.packages\n.pub/\n.dart_tool/\nbuild/\npubspec.lock\nandroid/\nios/\n.gradle/\nbuild.gradle\ngradle/\ngradlew\ngradlew.bat\nlocal.properties\nredux.iml\npubspec.lock\nandroid/local.properties\n\n# docs\nnode_modules/\n"
  },
  {
    "path": ".metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b\n  channel: stable\n\nproject_type: package\n"
  },
  {
    "path": ".travis.yml",
    "content": "os:\n    - linux\nsudo: false\naddons:\n    apt:\n        sources:\n            - ubuntu-toolchain-r-test\n        packages:\n            - libstdc++6\n            # - fonts-droid\nbefore_script:\n    - git clone https://github.com/flutter/flutter.git -b stable --depth 1\n    - ./flutter/bin/flutter doctor\nscript:\n    - ./flutter/bin/flutter test --coverage --coverage-path=lcov.info\nafter_success:\n    - bash <(curl -s https://codecov.io/bash)\ncache:\n    directories:\n        - $HOME/.pub-cache\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## [0.3.7]\n- fix test .\n- revert issue 613\n\n## [0.3.6]\n- fix some dos .\n\n## [0.3.5]\n- fix deprecated using of api - inheritFromWidgetOfExactType .\n\n## [0.3.4]\n- fix PureViewComponent's dispatch bug\n\n## [0.3.3]\n- fix Page name duplication\n- easy to use reselect\n- add keep-alive-client wrapper\n- fix spell error on pure-view-widget\n- add default widget for component.buildComponent api\n- add docsify to view docs\n- add unit test for collection utils and maplike connector\n\n## [0.3.2]\n- fix Page name duplication\n\n## [0.3.1]\n- rename AdapterSource’s api \n- add PureViewComponent \n- deprecate broadcastEffect \n- \n## [0.3.0]\n- add SourceFlowAdapter\n- Use SourceFlowAdapter instead of DynamicFlowAdapter in example\n- deprecate DynamicFlowAdapter\n\n## [0.2.8]\n- fix item-bean clone bug #493\n\n## [0.2.7]\n- add StateKey #461\n- reselect optimization #482\n\n## [0.2.6]\n- add TickerProviderMixin\n- let dispatch return whatever result in effect. #462 \n- fix Reselect's _listEquals bug #457 \n- fix SingleTickerProviderMixin & TickerProviderMixin’s dispose bug #461 \n- add ClearOnDependenciesChanged \n\n## [0.2.5]\n- add ctx.listen api\n- rename LocalState to LocalProps\n- correct some comments \n\n## [0.2.4]\n- fix Context.broadcast bug #375\n- fix PrivateReducerMixin bug #380 \n- add LocalState\n\n## [0.2.3]\n- Reconstruct Context\n- Breaking-change \n  - Reconstruct dependencies\n  - Remove OOP style\n  - Remove higherEffect\n  - Remove deprecated api(Connector, createMixedStore, AutoDispose:follow, AutoDispose:follower)\n  - Remove unused DisposedException\n- Hide widgets.dart's Action to compate with flutter1.7\n- Compate with flutter_web\n\n## [0.2.2]\n- add congruent conn \n- fields mainCtx & viewUpdater in ComponentState become public \n- fix bug when a store recived action after teardown\n\n## [0.2.1]\n- add forceUpdate api on context\n- fix bug in adapter’s appear & disappear if items are recycled\n- fix bug in connectStores api if page has no reducer\n\n## [0.2.0]\n- force update if driven by outside observable \n- fix inverter bugs & optimization connectStores api \n- modify the use of global state in example \n\n## [0.1.9]\n- add mixed-store's batch notification feature\n\n## [0.1.8]\n- add api to subscribe app-store for page-store\n- add api to subscribe app-store for component\n- add viewMiddleware\n- add adapterMiddleware\n- add effectMiddleware\n- add protected attribute method, more friendly to OOP\n- remove debug_report\n\n## [0.1.7]\n- reconstruct mixed-store\n- reconstruct communication\n- rename appBroadcast to broadcast\n- rename pageBroadcast to broadcastEffect\n- add dispatch-bus\n- enhance dispatch-api\n- add some docs\n- move test to dev_dependencies\n\n## [0.1.6]\n- fix bug if component has no reducer in app-routes\n- reconstruct createStore\n- app-routes's store to be visible\n\n## [0.1.5]\n- fix bug if notified on building\n- reconstruct test\n- add mergeMiddleware\\$\n\n## [0.1.4]\n- add support for AppStore\n- add routes\n- move middleware/aop to the top dir\n- add PrivateReducerMixin\n- add reselect\n- add docs\n\n## [0.1.3]\n- add support for immutable-state #111\n- fix the same type of state component reuse in listview #107\n- remove warnings in logMiddleware for debug-actions #98\n- correct spelling\n- modify bindAction #73\n\n## [0.1.2]\n- add stfState field in Context #58\n- add batchedNotify feature in page-store\n- add some docs\n- correct spelling\n\n## [0.1.1]\n- fix hot-reload bug\n- add excluedSelf in broadcast\n- rename sample to example\n- rename docs to doc\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2019 Alibaba, Inc.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\"><img src=\"https://img.alicdn.com/tfs/TB1r74NJyLaK1RjSZFxXXamPFXa-1024-1024.png\" align=\"center\" width=\"175\"></p>\n<h1 align=\"center\">Fish Redux</h1>\n\n[![Build Status](https://travis-ci.org/alibaba/fish-redux.svg?branch=master)](https://travis-ci.org/alibaba/fish-redux) [![pub package](https://img.shields.io/pub/v/fish_redux.svg)](https://pub.dartlang.org/packages/fish_redux) [![codecov](https://codecov.io/gh/alibaba/fish-redux/branch/master/graph/badge.svg)](https://codecov.io/gh/alibaba/fish-redux)\n\n## What is Fish Redux ?\n\nFish Redux is an assembled flutter application framework based on Redux state management.\nIt is suitable for building medium and large applications.\n\nIt has four characteristics:\n\n> 1. Functional Programming\n\n> 2. Predictable state container\n\n> 3. Pluggable componentization\n\n> 4. Non-destructive performance\n\n## Architecture diagram\n\n<img src=\"https://img.alicdn.com/tfs/TB1pkhoJr2pK1RjSZFsXXaNlXXa-1004-1370.png\" width=\"500px\" height=\"680px\">\n\n## Installation\n\n[Go](https://pub.dartlang.org/packages/fish_redux#-installing-tab-)\n\n## Documentation\n\nLanguage: [English](doc/README.md) | [中文简体](doc/README-cn.md)\n\n## Examples\n\n-   [todo list](example) - a simple todo list demo.\n-   run it:\n\n```\ncd ./example\nflutter create .\nflutter run\n```\n\n## What's the difference between 'Fish Redux' and 'Redux' ?\n\n-   [answers](doc/concept/what's-the-diiference.md)\n\n## Plugins\n\n### Code Template\n\n-   [Fish Redux Template For Android Studio](https://github.com/BakerJQ/FishReduxTemplateForAS), by BakerJQ.\n-   [Fish Redux Template For VSCode](https://github.com/huangjianke/fish-redux-template), by huangjianke.\n\n### Dev-Tools\n\n-   Redux Inspector (using [Flutter Debugger](https://github.com/blankapp/flutter-debugger) and [flipperkit_fish_redux_middleware](https://pub.dartlang.org/packages/flipperkit_fish_redux_middleware)) for Fish Redux apps, by [JianyingLi](https://github.com/lijy91)\n\n## License\n\n-   Fish Redux is released under the Apache 2.0 license. See [LICENSE](LICENSE) for details.\n\n\n## 关于我们\n阿里巴巴-闲鱼技术是国内最早也是最大规模线上运行Flutter的团队。\n\n我们在公众号中为你精选了Flutter独家干货，全面而深入。\n\n内容包括：Flutter的接入、规模化应用、引擎探秘、工程体系、创新技术等教程和开源信息。\n\n**架构／服务端／客户端／前端／质量工程师 在公众号中投递简历，名额不限哦**\n\n欢迎来闲鱼做一个好奇、幸福、有影响力的程序员，简历投递：tino.wjf@alibaba-inc.com\n\n订阅地址\n\n<img src=\"https://img.alicdn.com/tfs/TB17Ki5XubviK0jSZFNXXaApXXa-656-656.png\" width=\"328px\" height=\"328px\">\n\n[For English](https://twitter.com/xianyutech \"For English\")\n"
  },
  {
    "path": "analysis_options.yaml",
    "content": "# Specify analysis options.\n#\n# Until there are meta linter rules, each desired lint must be explicitly enabled.\n# See: https://github.com/dart-lang/linter/issues/288\n#\n# For a list of lints, see: http://dart-lang.github.io/linter/lints/\n# See the configuration guide for more\n# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer\n#\n# There are four similar analysis options files in the flutter repos:\n#   - analysis_options.yaml (this file)\n#   - packages/flutter/lib/analysis_options_user.yaml\n#   - https://github.com/flutter/plugins/blob/master/analysis_options.yaml\n#   - https://github.com/flutter/engine/blob/master/analysis_options.yaml\n#\n# This file contains the analysis options used by Flutter tools, such as IntelliJ,\n# Android Studio, and the `flutter analyze` command.\n#\n# The flutter/plugins repo contains a copy of this file, which should be kept\n# in sync with this file.\n\nanalyzer:\n    language:\n        enableSuperMixins: true\n    strong-mode:\n        implicit-dynamic: false\n    errors:\n        # treat missing required parameters as a warning (not a hint)\n        missing_required_param: warning\n        # treat missing returns as a warning (not a hint)\n        missing_return: warning\n        # allow having TODOs in the code\n        todo: ignore\n\n    exclude:\n        - 'bin/cache/**'\n        - 'flutter/**'\n        # the following two are relative to the stocks example and the flutter package respectively\n        # see https://github.com/dart-lang/sdk/issues/28463\n        - 'lib/i18n/stock_messages_*.dart'\n        - 'lib/src/http/**'\n\nlinter:\n    rules:\n        # these rules are documented on and in the same order as\n        # the Dart Lint rules page to make maintenance easier\n        # https://github.com/dart-lang/linter/blob/master/example/all.yaml\n        - always_declare_return_types\n        - always_put_control_body_on_new_line\n        # - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219\n        - always_require_non_null_named_parameters\n        - always_specify_types\n        - annotate_overrides\n        # - avoid_annotating_with_dynamic # conflicts with always_specify_types\n        - avoid_as\n        # - avoid_bool_literals_in_conditional_expressions # not yet tested\n        # - avoid_catches_without_on_clauses # we do this commonly\n        # - avoid_catching_errors # we do this commonly\n        - avoid_classes_with_only_static_members\n        - avoid_empty_else\n        - avoid_function_literals_in_foreach_calls\n        - avoid_init_to_null\n        - avoid_null_checks_in_equality_operators\n        # - avoid_positional_boolean_parameters # not yet tested\n        # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)\n        - avoid_relative_lib_imports\n        - avoid_renaming_method_parameters\n        - avoid_return_types_on_setters\n        # - avoid_returning_null # we do this commonly\n        # - avoid_returning_this # https://github.com/dart-lang/linter/issues/842\n        # - avoid_setters_without_getters # not yet tested\n        # - avoid_single_cascade_in_expression_statements # not yet tested\n        - avoid_slow_async_io\n        # - avoid_types_as_parameter_names # https://github.com/dart-lang/linter/pull/954/files\n        # - avoid_types_on_closure_parameters # conflicts with always_specify_types\n        # - avoid_unused_constructor_parameters # https://github.com/dart-lang/linter/pull/847\n        - await_only_futures\n        - camel_case_types\n        - cancel_subscriptions\n        # - cascade_invocations # not yet tested\n        # - close_sinks # https://github.com/flutter/flutter/issues/5789\n        # - comment_references # blocked on https://github.com/dart-lang/dartdoc/issues/1153\n        # - constant_identifier_names # https://github.com/dart-lang/linter/issues/204\n        - control_flow_in_finally\n        - directives_ordering\n        - empty_catches\n        - empty_constructor_bodies\n        - empty_statements\n        - hash_and_equals\n        - implementation_imports\n        # - invariant_booleans # https://github.com/flutter/flutter/issues/5790\n        - iterable_contains_unrelated_type\n        # - join_return_with_assignment # not yet tested\n        - library_names\n        - library_prefixes\n        - list_remove_unrelated_type\n        # - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791\n        - no_adjacent_strings_in_list\n        - no_duplicate_case_values\n        - non_constant_identifier_names\n        # - omit_local_variable_types # opposite of always_specify_types\n        # - one_member_abstracts # too many false positives\n        # - only_throw_errors # https://github.com/flutter/flutter/issues/5792\n        - overridden_fields\n        - package_api_docs\n        - package_names\n        - package_prefixed_library_names\n        # - parameter_assignments # we do this commonly\n        - prefer_adjacent_string_concatenation\n        - prefer_asserts_in_initializer_lists\n        - prefer_bool_in_asserts\n        - prefer_collection_literals\n        - prefer_conditional_assignment\n        - prefer_const_constructors\n        - prefer_const_constructors_in_immutables\n        - prefer_const_declarations\n        - prefer_const_literals_to_create_immutables\n        # - prefer_constructors_over_static_methods # not yet tested\n        - prefer_contains\n        # - prefer_equal_for_default_values # not yet tested\n        # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods\n        - prefer_final_fields\n        - prefer_final_locals\n        - prefer_foreach\n        # - prefer_function_declarations_over_variables # not yet tested\n        - prefer_initializing_formals\n        # - prefer_interpolation_to_compose_strings # not yet tested\n        - prefer_is_empty\n        - prefer_is_not_empty\n        - prefer_single_quotes\n        - prefer_typing_uninitialized_variables\n        - recursive_getters\n        - slash_for_doc_comments\n        # - sort_constructors_first\n        - sort_unnamed_constructors_first\n        - super_goes_last\n        - test_types_in_equals\n        - throw_in_finally\n        # - type_annotate_public_apis # subset of always_specify_types\n        - type_init_formals\n        # - unawaited_futures # https://github.com/flutter/flutter/issues/5793\n        - unnecessary_brace_in_string_interps\n        - unnecessary_getters_setters\n        # - unnecessary_lambdas # https://github.com/dart-lang/linter/issues/498\n        - unnecessary_null_aware_assignments\n        - unnecessary_null_in_if_null_operators\n        - unnecessary_overrides\n        # - unnecessary_parenthesis\n        # - unnecessary_statements # not yet tested\n        - unnecessary_this\n        - unnecessary_new\n        - unnecessary_const\n        - unrelated_type_equality_checks\n        - use_rethrow_when_possible\n        # - use_setters_to_change_properties # not yet tested\n        # - use_string_buffers # https://github.com/dart-lang/linter/pull/664\n        # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review\n        - valid_regexps\n"
  },
  {
    "path": "doc/README-cn.md",
    "content": "-   **简介**\n    -   [关于](introduction/README-cn.md)\n    -   [演进](concept/evolution-of-fish-redux.md)\n    -   [特性](concept/features.md)\n-   **核心概念**\n    -   [Redux](concept/redux-cn.md)\n        -   [Action](concept/action-cn.md)\n        -   [Connector](concept/connector-cn.md)\n        -   [Reducer](concept/reducer-cn.md)\n        -   [Middleware](concept/middleware-cn.md)\n    -   [Component](concept/component-cn.md)\n        -   [View](concept/view-cn.md)\n        -   [Reducer](concept/reducer-cn.md)\n        -   [Effect](concept/effect-cn.md)\n        -   [HigherEffect](concept/higher-effect-cn.md)\n        -   [Lifecycle](concept/lifecycle-cn.md)\n        -   [Dependencies](concept/dependencies-cn.md)\n        -   [Dependent](concept/dependent-cn.md)\n        -   [ShouldUpdate](concept/should-update-cn.md)\n        -   [OnError](concept/on-error-cn.md)\n        -   [Filter](concept/filter-cn.md)\n        -   [OOP](concept/oop-cn.md)\n        -   [WidgetWrapper](concept/widget-wrapper-cn.md)\n        -   [Page](concept/page-cn.md)\n    -   [Adapter](concept/adapter-cn.md)\n        -   [StaticFlowAdapter](concept/static-flow-adapter-cn.md)\n        -   [DynamicFlowAdapter](concept/dynamic-flow-adapter-cn.md)\n        -   [CustomAdapter](concept/custom-adapter-cn.md)\n-   **其他**\n    -   [What's the difference between 'Fish Redux' and 'Redux' ?](concept/what's-the-diiference-cn.md)\n    -   [What's-adapter](concept/what's-adapter.md)\n    -   [What's-connector](concept/what's-connector.md)\n    -   [Mechanism](concept/mechanism-cn.md)\n    -   [Directory](concept/directory-cn.md)\n"
  },
  {
    "path": "doc/README.md",
    "content": "- **Introduction**\n  - [About](introduction/README.md)\n  - [Evolution](concept/evolution-of-fish-redux.md)\n  - [Features](concept/features.md)\n- **Core Concepts**\n  - [Redux](concept/redux.md)\n    - [Action](concept/action.md)\n    - [Connector](concept/connector.md)\n    - [Reducer](concept/reducer.md)\n    - [Middleware](concept/middleware.md)\n  - [Component](concept/component.md)\n    - [View](concept/view.md)\n    - [Reducer](concept/reducer.md)\n    - [Effect](concept/effect.md)\n    - [HigherEffect](concept/higher-effect.md)\n    - [Lifecycle](concept/lifecycle.md)\n    - [Dependencies](concept/dependencies.md)\n    - [Dependent](concept/dependent.md)\n    - [ShouldUpdate](concept/should-update.md)\n    - [OnError](concept/on-error.md)\n    - [Filter](concept/filter.md)\n    - [OOP](concept/oop.md)\n    - [WidgetWrapper](concept/widget-wrapper.md)\n    - [Page](concept/page.md)\n  - [Adapter](concept/adapter.md)\n    - [StaticFlowAdapter](concept/static-flow-adapter.md)\n    - [DynamicFlowAdapter](concept/dynamic-flow-adapter.md)\n    - [CustomAdapter](concept/custom-adapter.md)\n- **Others**\n  - [What's the difference between 'Fish Redux' and 'Redux' ?](concept/what's-the-diiference.md)\n  - [What is an adapter?](concept/what's-adapter.md)\n  - [What is a connector?](concept/what's-connector.md)\n  - [Communication mechanism of fish-redux](concept/mechanism.md)\n  - [Recommended directory structure](concept/directory.md)\n"
  },
  {
    "path": "doc/concept/action-cn.md",
    "content": "# Action\n\n-   Action 包含两个字段\n    -   type\n    -   payload\n-   推荐的写法是\n    -   为一个组件|适配器创建一个 action.dart 文件，包含两个类\n        -   为 type 字段起一个枚举类\n        -   为 Action 的创建起一个 ActionCreator 类，这样利于约束 payload 的类型。\n    -   Effect 接受处理的 Action，以 on{Verb} 命名\n    -   Reducer 接受处理的 Action，以{verb} 命名\n    -   示例代码\n\n```dart\nenum MessageAction {\n    onShare,\n    shared,\n}\n\nclass MessageActionCreator {\n    static Action onShare(Map<String, Object> payload) {\n        return Action(MessageAction.onShare, payload: payload);\n    }\n\n    static Action shared() {\n        return const Action(MessageAction.shared);\n    }\n}\n```\n"
  },
  {
    "path": "doc/concept/action.md",
    "content": "# Action\n\n-   Action contains two fields\n    -   type\n    -   payload\n-   Recommended way of writing action\n    -   Create an action.dart file for a component|adapter that contains two classes\n        -   An enumeration class for the type field\n        -   An ActionCreator class is created for the creator of the Action, which helps to constrain the type of payload.\n    -   Effect Accepted Action which's type is named after `on{verb}`\n    -   Reducer Accepted Action which's type is named after `{verb}`\n    -   Sample code\n\n```dart\nenum MessageAction {\n    onShare,\n    shared,\n}\n\nclass MessageActionCreator {\n    static Action onShare(Map<String, Object> payload) {\n        return Action(MessageAction.onShare, payload: payload);\n    }\n\n    static Action shared() {\n        return const Action(MessageAction.shared);\n    }\n}\n```\n"
  },
  {
    "path": "doc/concept/adapter-cn.md",
    "content": "# Adapter\n\n-   我们在基础 Component 的概念外，额外增加了一种组件化的抽象 Adapter。它的目标是解决 Component 模型在 ListView 的场景下的 3 个问题\n    -   1）将一个\"Big-Cell\"放在 ListView 里，无法享受 ListView 代码的性能优化。\n    -   2）Component 无法区分 appear|disappear 和 init|dispose 事件。\n    -   3）Effect 的生命周期和 View 的耦合，在 ListView 的有些场景下不符合直观的预期。\n-   一个 Adapter 和 Component 几乎都是一致的，除了以下几点\n    -   Component 生成一个 Widget，Adapter 生成一个 ListAdapter，ListAdapter 有能力生成一组 Widget。\n        -   不具体生成 Widget，而是一个 ListAdapter，能非常大的提升页面帧率和流畅度。\n    -   Effect-Lifecycle-Promote\n        -   Component 的 Effect 是跟着 Widget 的生命周期走的，Adapter 的 Effect 是跟着上一级的 Widget 的生命周期走。\n        -   Effect​ 提升，极大的解除了业务逻辑和视图生命的耦合，即使它的展示还未出现，的其他模块依然能通过 dispatch-api，调用它的能力。\n    -   appear|disappear 的通知\n        -   由于 Effect 生命周期的提升，我们就能更加精细的区分 init|dispose 和 appear|disappear。而这在 Component 的模型中是无法区分的。\n    -   Reducer is long-lived, Effect is medium-lived, View is short-lived.\n-   Adapter 的三种实现\n    -   [DynamicFlowAdapter](dynamic-flow-adapter-cn.md)\n    -   [StaticFlowAdapter](static-flow-adapter-cn.md)\n    -   [CustomAdapter](custom-adapter-cn.md)\n"
  },
  {
    "path": "doc/concept/adapter.md",
    "content": "# Adapter\n\n-   In addition to the concept of the underlying Component, we have added a componentized abstract Adapter. Its goal is to solve the 3 problems of the Component model in the ListView scene.\n\n    -   1）Putting a \"Big-Cell\" in the ListView does not enjoy the performance optimization of the ListView code.\n    -   2）Component cannot distinguish between the appear|disappear and init|dispose events.\n    -   3）The life cycle of the Effect and the coupling of the View do not meet the intuitive expectations in some scenes of the ListView.\n\n-   An Adapter and a Component are almost identical except for the following points\n\n    -   Component generates a Widget, Adapter generates a ListAdapter, and ListAdapter has the ability to generate a list of Widgets.。\n        -   Not specifically generating a Widget but a ListAdapter can greatly improve the page frame rate and fluency.\n    -   Effect-Lifecycle-Promote\n        -   The Effect of Component follows the life cycle of the Widget, and the Adapter's Effect follows the life cycle of the parent Widget.\n        -   The improvement of the life cycle of the effect greatly removes the coupling between the business logic and the view life. Even if its display has not yet appeared, other modules can still call its capabilities through dispatch-api.\n    -   Appearance|disappear event notification\n        -   As the Effect lifecycle improves, we can more closely distinguish between init|dispose and appear|disappear. This is indistinguishable from the Model's model.\n    -   Reducer is long-lived, Effect is medium-lived, View is short-lived.\n\n-   Three implementations of Adapter\n    -   [DynamicFlowAdapter](dynamic-flow-adapter.md)\n    -   [StaticFlowAdapter](static-flow-adapter.md)\n    -   [CustomAdapter](custom-adapter.md)\n"
  },
  {
    "path": "doc/concept/auto-dispose-cn.md",
    "content": "# Auto-Dispose\n\n-   它是一个非常简易管理生命周期对象的方式。一个 auto-dispose 对象可以自我主动释放，或者在它 follow 的 托管对象释放的时候，释放。\n-   在 Effect 中使用的 Context，以及 HigherEffect 中的 EffectPart，都是 auto-dispose 对象。所以我们可以方便的将自定义的需要做生命周期管理的对象托管给它们。\n-   示例代码\n\n```dart\nclass ItemWidgetBindingObserver extends WidgetsBindingObserver\n    with AutoDispose {\n  ItemWidgetBindingObserver() : super() {\n    WidgetsBinding.instance.addObserver(this);\n  }\n\n  @override\n  void didChangeAppLifecycleState(AppLifecycleState state) {\n    if (AppConfig.flutterBinding.framesEnabled &&\n        state == AppLifecycleState.resumed) {\n      AppConfig.flutterBinding.performReassemble();\n    }\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n    WidgetsBinding.instance.removeObserver(this);\n  }\n}\n\nvoid _init(Action action, Context<ItemPageContainerState> ctx) {\n    final ItemWidgetBindingObserver observer = ItemWidgetBindingObserver();\n    observer.follow(ctx);\n}\n\n```\n"
  },
  {
    "path": "doc/concept/auto-dispose.md",
    "content": "# Auto-Dispose\n\n-   AutoDispose is a very simple way to manage lifecycle objects. An auto-dispose object can be released on its own initiative or released when the managed object it follows is released.\n-   The Context used in Effect and the EffectPart in HigherEffect are auto-dispose objects. So we can easily host custom objects that need to be managed for lifecycle management.\n-   Sample Code\n\n```dart\nclass ItemWidgetBindingObserver extends WidgetsBindingObserver\n    with AutoDispose {\n  ItemWidgetBindingObserver() : super() {\n    WidgetsBinding.instance.addObserver(this);\n  }\n\n  @override\n  void didChangeAppLifecycleState(AppLifecycleState state) {\n    if (AppConfig.flutterBinding.framesEnabled &&\n        state == AppLifecycleState.resumed) {\n      AppConfig.flutterBinding.performReassemble();\n    }\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n    WidgetsBinding.instance.removeObserver(this);\n  }\n}\n\nvoid _init(Action action, Context<ItemPageContainerState> ctx) {\n    final ItemWidgetBindingObserver observer = ItemWidgetBindingObserver();\n    observer.follow(ctx);\n}\n\n```\n"
  },
  {
    "path": "doc/concept/component-cn.md",
    "content": "# Component\n\n组件是对视图展现和逻辑功能的封装。\n面向当下，从 Redux 的视角看，我们对组件分为状态修改的功能(Reducer)和其他。\n面向未来，从 UI-Automation 的视角看，我们对组件分为展现表达和其他。\n结合上面两个视角，于是我们得到了，View、 Effect、Reducer 三部分，称之为组件的三要素，分别负责了组件的展示、非修改数据的行为、修改数据的操作。\n\n我们以显式配置的方式来完成大组件所依赖的小组件、适配器的注册，这份依赖配置称之为 Dependencies。\n\n所以有了这个公式\nComponent = View + Effect(可选) + Reducer(可选) + Dependencies(可选)\n\n分治：从组件的角度\n<img src=\"https://img.alicdn.com/tfs/TB1vqB2J4YaK1RjSZFnXXa80pXa-900-780.png\" width=\"450px\" height=\"390px\">\n\n集中：从 Store 的角度\n<img src=\"https://img.alicdn.com/tfs/TB1sThMJYvpK1RjSZFqXXcXUVXa-1426-762.png\" width=\"713px\" height=\"381px\">\n"
  },
  {
    "path": "doc/concept/component.md",
    "content": "# Component\n\nComponent is the encapsulation of view presentation and logic functions.\nFor the moment, from the perspective of Redux, we divide the component into state-manage functions (Reducers) and others.\nLooking to the future, from the perspective of UI-Automation, we divide the component into presentations and others.\n\nCombining the above two perspectives, we got the three parts of View, SideEffect, and Reducer, which are called the three factors of the component.\n\nWe use explicit configuration to complete the registration of components and adapters on which large component depend. This dependency configuration is called Dependencies.\n\nSo with this formula:\nComponent = View + Effect(Optional) + Reducer(Optional) + Dependencies(Optional)\n\nDivision: From the perspective of the component\n<img src=\"https://img.alicdn.com/tfs/TB1vqB2J4YaK1RjSZFnXXa80pXa-900-780.png\" width=\"450px\" height=\"390px\">\n\nConcentration: From the perspective of the Store\n<img src=\"https://img.alicdn.com/tfs/TB1sThMJYvpK1RjSZFqXXcXUVXa-1426-762.png\" width=\"713px\" height=\"381px\">\n"
  },
  {
    "path": "doc/concept/connector-cn.md",
    "content": "# Connector<T, P>\n\n-   它表达了如何从一个大数据中读取小数据，同时对小数据的修改如何同步给大数据，这样的数据连接关系。\n-   它是将一个集中式的 Reducer，可以由多层次多模块的小 Reducer 自动拼装的关键。\n    -   它大大降低了我们使用 Redux 的复杂度。我们不再关心组装过程，我们关心的核心是什么动作促使数据怎么变化。\n-   它使用在配置 Dependencies 中，在配置中我们就固化了大组件和小组件之间的连接关系(数据管道)，所以在我们使用小组件的时候是不需要传入任何动态参数的。\n-   ![image.png | left | 719x375](https://cdn.nlark.com/lark/0/2018/png/82574/1545365202743-01074be7-f067-45c7-aae0-91b12cd50ae6.png)\n\n-   Sample Code\n\n```dart\nclass DetialState {\n    Profile profile;\n    String message;\n}\n\nConnOp<DetialState, String> messageConnector() {\n    return ConnOp<DetialState, String>(\n        get: (DetialState state) => state.message,\n        set: (DetialState state, String message) => state.message = message,\n    );\n}\n```\n"
  },
  {
    "path": "doc/concept/connector.md",
    "content": "# Connector<T, P>\n\n-   It expresses a data connection relationship of how to read small data from a big data, and how to synchronize to big data when the small data is modified。\n-   It is the key to a centralized Reducer that can be assembled automatically by a multi-level, multi-module, small Reducer\n    -   It greatly reduces the difficulty of using Redux. We no longer care about the assembly process, we care about what specific actions cause the state to change.\n-   It is used in the configuration Dependencies, in the configuration we have solidified the connection between the large component and the small component, so we do not need to pass in any dynamic parameters when we use the small component.\n-   ![image.png | left | 719x375](https://cdn.nlark.com/lark/0/2018/png/82574/1545365202743-01074be7-f067-45c7-aae0-91b12cd50ae6.png)\n\n-   Sample Code\n\n```dart\nclass DetialState {\n    Profile profile;\n    String message;\n}\n\nConnOp<DetialState, String> messageConnector() {\n    return ConnOp<DetialState, String>(\n        get: (DetialState state) => state.message,\n        set: (DetialState state, String message) => state.message = message,\n    );\n}\n```\n"
  },
  {
    "path": "doc/concept/custom-adapter-cn.md",
    "content": "# CustomAdapter\n\n-   对大 Cell 的自定义实现\n-   要素和 Component 类似，不一样的地方是 Adapter 的视图部分返回的是一个 ListAdapter\n-   示例代码\n\n```dart\nclass CommentAdapter extends Adapter<CommentState> {\n    CommentAdapter()\n        : super(\n            adapter: buildCommentAdapter,\n            effect: buildCommentEffect(),\n            reducer: buildCommentReducer(),\n        );\n}\n\nListAdapter buildCommentAdapter(CommentState state, Dispatch dispatch, ViewService service) {\n    final List<IndexedWidgetBuilder> builders = Collections.compact(<IndexedWidgetBuilder>[]\n    ..add((BuildContext buildContext, int index) =>\n        _buildDetailCommentHeader(state, dispatch, service))\n    ..addAll(_buildCommentViewList(state, dispatch, service))\n    ..add(isEmpty(state.commentListRes?.items)\n        ? (BuildContext buildContext, int index) =>\n            _buildDetailCommentEmpty(state.itemInfo, dispatch)\n        : null)\n    ..add(state.commentListRes?.getHasMore() == true\n        ? (BuildContext buildContext, int index) => _buildLoadMore(dispatch)\n        : null));\n    return ListAdapter(\n    (BuildContext buildContext, int index) =>\n        builders[index](buildContext, index),\n    builders.length,\n    );\n}\n\n///builds\n```\n"
  },
  {
    "path": "doc/concept/custom-adapter.md",
    "content": "# CustomAdapter\n\n-   Custom implementation of large Cell in LisView.\n-   The Factors of the Adapter are similar to the Component's. The difference is that the view part of the Adapter returns a ListAdapter.\n-   Sample Code\n\n```dart\nclass CommentAdapter extends Adapter<CommentState> {\n    CommentAdapter()\n        : super(\n            adapter: buildCommentAdapter,\n            effect: buildCommentEffect(),\n            reducer: buildCommentReducer(),\n        );\n}\n\nListAdapter buildCommentAdapter(CommentState state, Dispatch dispatch, ViewService service) {\n    final List<IndexedWidgetBuilder> builders = Collections.compact(<IndexedWidgetBuilder>[]\n    ..add((BuildContext buildContext, int index) =>\n        _buildDetailCommentHeader(state, dispatch, service))\n    ..addAll(_buildCommentViewList(state, dispatch, service))\n    ..add(isEmpty(state.commentListRes?.items)\n        ? (BuildContext buildContext, int index) =>\n            _buildDetailCommentEmpty(state.itemInfo, dispatch)\n        : null)\n    ..add(state.commentListRes?.getHasMore() == true\n        ? (BuildContext buildContext, int index) => _buildLoadMore(dispatch)\n        : null));\n    return ListAdapter(\n    (BuildContext buildContext, int index) =>\n        builders[index](buildContext, index),\n    builders.length,\n    );\n}\n\n///builds\n```\n"
  },
  {
    "path": "doc/concept/dependencies-cn.md",
    "content": "# Dependencies\n\n-   Dependencies 是一个表达组件之间依赖关系的结构。它接收两个字段\n    -   slots\n        -   <String, [Dependent](dependent-cn.md)>{}\n    -   [adapter](adapter-cn.md)\n-   它主要包含三方面的信息\n    -   slots，组件依赖的插槽。\n    -   adapter，组件依赖的具体适配器（用来构建高性能的 ListView）。\n    -   [Dependent](dependent-cn.md) 是 subComponent | subAdapter + [connector](connector-cn.md) 的组合。\n    -   一个 组件的 [Reducer](reducer-cn.md) 由 Component 自身配置的 Reducer 和它的 Dependencies 下的所有子 Reducers 自动复合而成。\n-   示例代码\n\n```dart\n///register in component\nclass ItemComponent extends ItemComponent<ItemState> {\n  ItemComponent()\n      : super(\n          view: buildItemView,\n          reducer: buildItemReducer(),\n          dependencies: Dependencies<ItemState>(\n            slots: <String, Dependent<ItemState>>{\n              'appBar': AppBarComponent().asDependent(AppBarConnector()),\n              'body': ItemBodyComponent().asDependent(ItemBodyConnector()),\n              'ad_ball': ADBallComponent().asDependent(ADBallConnector()),\n              'bottomBar': BottomBarComponent().asDependent(BottomBarConnector()),\n            },\n          ),\n        );\n}\n\n///call in view\nWidget buildItemView(ItemState state, Dispatch dispatch, ViewService service) {\n  return Scaffold(\n      body: Stack(\n        children: <Widget>[\n          service.buildComponent('body'),\n          service.buildComponent('ad_ball'),\n          Positioned(\n            child: service.buildComponent('bottomBar'),\n            left: 0.0,\n            bottom: 0.0,\n            right: 0.0,\n            height: 100.0,\n          ),\n        ],\n      ),\n      appBar: AppbarPreferSize(child: service.buildComponent('appBar')));\n}\n```\n"
  },
  {
    "path": "doc/concept/dependencies.md",
    "content": "# Dependencies\n\n-   Dependencies is a structure that expresses dependencies between components. It accepts two fields\n    -   slots\n        -   <String, [Dependent](dependent.md)>{}\n    -   [adapter](adapter.md)\n-   It mainly contains three aspects of information\n    -   The slots that the component depends on.\n    -   The adapter that the component depends on (used to build a high-performance ListView).\n    -   [Dependent](dependent.md) Is a combination of subComponent | subAdapter + [connector](connector.md)。\n    -   A component's [Reducer](reducer.md) is automatically compounded by the Reducer configured by the Component itself and all of the Reducers under its Dependencies.\n-   Sample Code\n\n```dart\n///register in component\nclass ItemComponent extends ItemComponent<ItemState> {\n  ItemComponent()\n      : super(\n          view: buildItemView,\n          reducer: buildItemReducer(),\n          dependencies: Dependencies<ItemState>(\n            slots: <String, Dependent<ItemState>>{\n              'appBar': AppBarComponent().asDependent(AppBarConnector()),\n              'body': ItemBodyComponent().asDependent(ItemBodyConnector()),\n              'ad_ball': ADBallComponent().asDependent(ADBallConnector()),\n              'bottomBar': BottomBarComponent().asDependent(BottomBarConnector()),\n            },\n          ),\n        );\n}\n\n///call in view\nWidget buildItemView(ItemState state, Dispatch dispatch, ViewService service) {\n  return Scaffold(\n      body: Stack(\n        children: <Widget>[\n          service.buildComponent('body'),\n          service.buildComponent('ad_ball'),\n          Positioned(\n            child: service.buildComponent('bottomBar'),\n            left: 0.0,\n            bottom: 0.0,\n            right: 0.0,\n            height: 100.0,\n          ),\n        ],\n      ),\n      appBar: AppbarPreferSize(child: service.buildComponent('appBar')));\n}\n```\n"
  },
  {
    "path": "doc/concept/dependent-cn.md",
    "content": "### Dependent\n\n-   Dependent = connector<T, P> + subComponent | subAdapter 的组合，它表达了小组件|小适配器是如何连接到 Component 的。\n-   示例代码\n\n```dart\n/// todo\n```\n"
  },
  {
    "path": "doc/concept/dependent.md",
    "content": "### Dependent\n\n-   Dependent = connector<T, P> + subComponent | subAdapter\n-   It expresses how the small component or adapter are connected to it's parent component.\n-   Sample Code\n\n```dart\n/// todo\n```\n"
  },
  {
    "path": "doc/concept/directory-cn.md",
    "content": "# Directory\n\n推荐的目录结构会是这样\n\n```\nsample_page\n    -- action.dart /// define action types and action creator\n    -- page.dart /// config a page or component\n    -- view.dart /// define a function which expresses the presentation of user interface\n    -- effect.dart /// define a function which handles the side-effect\n    -- reducer.dart /// define a function which handles state-change\n    -- state.dart /// define a state and some connector of substate\n    components\n        sample_component\n        -- action.dart\n        -- component.dart\n        -- view.dart\n        -- effect.dart\n        -- reducer.dart\n        -- state.dart\n```\n\n上层负责组装，下层负责实现。\n"
  },
  {
    "path": "doc/concept/directory.md",
    "content": "# Directory\n\nThe recommended directory structure\n\n```\nsample_page\n    -- action.dart /// define action types and action creator\n    -- page.dart /// config a page or component\n    -- view.dart /// define a function which expresses the presentation of user interface\n    -- effect.dart /// define a function which handles the side-effect\n    -- reducer.dart /// define a function which handles state-change\n    -- state.dart /// define a state and some connector of substate\n    components\n        sample_component\n        -- action.dart\n        -- component.dart\n        -- view.dart\n        -- effect.dart\n        -- reducer.dart\n        -- state.dart\n```\n\nThe upper layer is responsible for assembly and the lower layer is responsible for implementation.\n"
  },
  {
    "path": "doc/concept/dynamic-flow-adapter-cn.md",
    "content": "# DynamicFlowAdapter\n\n-   模版是一个 Map，接受一个数组类型的数据驱动\n-   示例代码\n\n```dart\nclass RecommendAdapter extends DynamicFlowAdapter<RecommendState> {\n    RecommendAdapter()\n        : super(\n            pool: <String, Component<Object>>{\n                'card_0': RecommendTitleComponent(),\n                'card_1': RecommendRowComponent(),\n            },\n            connector: RecommendCardListConnector(),\n        );\n}\n```\n<img src=\"https://img.alicdn.com/tfs/TB10lxHLMDqK1RjSZSyXXaxEVXa-1838-1024.png\" width=\"919px\" height=\"512px\">\n"
  },
  {
    "path": "doc/concept/dynamic-flow-adapter.md",
    "content": "# DynamicFlowAdapter\n\n-   The template is a Map that accepts an array-like data driven\n-   Sample Code\n\n```dart\nclass RecommendAdapter extends DynamicFlowAdapter<RecommendState> {\n    RecommendAdapter()\n        : super(\n            pool: <String, Component<Object>>{\n                'card_0': RecommendTitleComponent(),\n                'card_1': RecommendRowComponent(),\n            },\n            connector: RecommendCardListConnector(),\n        );\n}\n```\n\n<img src=\"https://img.alicdn.com/tfs/TB10lxHLMDqK1RjSZSyXXaxEVXa-1838-1024.png\" width=\"919px\" height=\"512px\">\n"
  },
  {
    "path": "doc/concept/effect-cn.md",
    "content": "# Effect\n\nEffect顾名思义，用于处理Action的副作用。\n\n我估摸着有人就要问我了，副作用是啥玩意？\n\n打个比方吧，假如我拥有一个函数 `f()`\n\n```text\nfn f(x):\n  return x * 1\n```\n\n此时此刻，另一个函数 `g()`\n\n```text\nfn g(x):\n  changeSystemEntropy()\n  return ax ^ 2 + bx + c\n```\n\n我们可以发现，`g()`里边有个改变系统熵的行为。这在函数式编程思想中，就叫做副作用，因为它可能影响到除了这个函数内部自身状态以外的其他状态。\n\n在Fish-Redux中同样，我们通过 `dispatch()` 一些action实现状态修改，但是相对于状态来说，对外部的操作，类似于 `SystemChrome.setSystemUIOverlayStyle()`这样的操作，都是副作用。\n\n现在介绍完了副作用，也没啥可介绍的了。\n\nEffect用法跟Reducer差不太多，但是作用完全不同。\n\n除了上面介绍的场景之外，异步请求也是一个经常会有的情况，这时候Effect可以帮你方便的解决这些问题。\n\n你可以通过控制effect的返回值来达到某些目的，默认情况下，effect会在reducer之前被执行。\n\n当前effect返回 `true` 的时候，就会停止后续的effect和reducer的操作\n\n当前effect返回 `false` 的时候，后续effect和reducer继续执行\n\n-   Effect 是一个处理所有副作用的函数。它接收下面的参数\n    -   Action action\n    -   Context context\n        -   BuildContext context\n        -   T state\n        -   dispatch\n        -   isDisposed\n        \nEffect会接收来自 View 的“意图”，包括对应的生命周期的回调，然后做出具体的执行。\n    -   它的处理可能是一个异步函数，数据可能在过程中被修改，所以我们应该通过 context.state 获取最新数据。\n    -   如果它要修改数据，应该发一个 Action 到 Reducer 里去处理。它对数据是只读的，不能直接去修改数据。\n    -   如果它的返回值是一个非空值，则代表自己优先处理，不再做下一步的动作；否则广播给其他组件的 Effect 部分，同时发送给 Reducer。\n\n> Self-First-Broadcast。\n> ![image.png | left | 747x399](https://cdn.nlark.com/lark/0/2018/png/82574/1545365233153-4c8105b4-050c-49e6-be02-dbf28a861caa.png)\n\n-   示例代码\n\n```dart\n/// one style of writing\nFutureOr<Object> sideEffect(Action action, Context<String> ctx) async {\n  if (action.type == Lifecycle.initState) {\n    //do something on initState\n    return true;\n  } else if (action.type == 'onShare') {\n    //do something on onShare\n    await Future<void>.delayed(Duration(milliseconds: 1000));\n    ctx.dispatch(const Action('shared'));\n    return true;\n  }\n  return null;\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: sideEffect,\n        );\n}\n```\n\n```dart\n/// another style of writing\nEffect<String> buildEffect() {\n  return combineEffects(<Object, Effect<String>>{\n    Lifecycle.initState: _initState,\n    'onShare': _onShare,\n  });\n}\n\nvoid _initState(Action action, Context<String> ctx) {\n  //do something on initState\n}\n\nvoid _onShare(Action action, Context<String> ctx) async {\n  //do something on onShare\n  await Future<void>.delayed(Duration(milliseconds: 1000));\n  ctx.dispatch(const Action('shared'));\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n        );\n}\n```\n"
  },
  {
    "path": "doc/concept/effect.md",
    "content": "# Effect\n\n-   Effect is a function that handles all side effects. It receives the following parameters\n    -   Action action\n    -   Context context\n        -   BuildContext context\n        -   T state\n        -   dispatch\n        -   isDisposed\n-   It mainly contains four aspects of information\n    -   Receive \"intent\" from the View, including the corresponding lifecycle callback, and then make specific execution.\n    -   Its processing may be an asynchronous function, the data may be changed in the process, so we should get the latest data through context.state.\n    -   If you want to modify the data, you should send an Action to the Reducer to handle. It is read-only for data and cannot be modified directly in a effect function.\n    -   If its return value is a non-null value, it will take precedence for itself and will not do the next step; otherwise it will broadcast to the Effect part of other components and sent the action to the Reducer.\n\n> Self-First-Broadcast。\n> ![image.png | left | 747x399](https://cdn.nlark.com/lark/0/2018/png/82574/1545365233153-4c8105b4-050c-49e6-be02-dbf28a861caa.png)\n\n-   Sample Code\n\n```dart\n/// one style of writing\nFutureOr<Object> sideEffect(Action action, Context<String> ctx) async {\n  if (action.type == Lifecycle.initState) {\n    //do something on initState\n    return true;\n  } else if (action.type == 'onShare') {\n    //do something on onShare\n    await Future<void>.delayed(Duration(milliseconds: 1000));\n    ctx.dispatch(const Action('shared'));\n    return true;\n  }\n  return null;\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: sideEffect,\n        );\n}\n```\n\n```dart\n/// another style of writing\nEffect<String> buildEffect() {\n  return combineEffects(<Object, Effect<String>>{\n    Lifecycle.initState: _initState,\n    'onShare': _onShare,\n  });\n}\n\nvoid _initState(Action action, Context<String> ctx) {\n  //do something on initState\n}\n\nvoid _onShare(Action action, Context<String> ctx) async {\n  //do something on onShare\n  await Future<void>.delayed(Duration(milliseconds: 1000));\n  ctx.dispatch(const Action('shared'));\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n        );\n}\n```\n"
  },
  {
    "path": "doc/concept/evolution-of-fish-redux.md",
    "content": "# fish-redux 的演进史\n\nfish-redux 是一个不断演进的框架，甚至是在不断的回炉重造，在这个过程中\n\n<img src=\"https://img.alicdn.com/tfs/TB1aeJELpzqK1RjSZFCXXbbxVXa-1794-938.png\" width=\"897px\" height=\"469px\">\n\n-   1. 第一个版本是基于社区内的 flutter_redux 进行的改造，核心是提供了 UI 代码的组件化，当然问题也非常明显，针对复杂的业务场景，往往业务逻辑很多，无法做到逻辑代码的分治和复用。\n\n-   2. 第二个版本针对第一个版本的问题，做出了比较重大的修改，解决了 UI 代码和逻辑代码的分治问题，但设计上打破了 redux 的原则，丢失了 Redux 的精华。\n\n-   3. 在第三个版本进行重构时，我们确立了整体的架构原则与分层要求，一方面按照 reduxjs 的代码进行了 flutter 侧的 redux 实现，将 redux 完整保留下来。另一方面针对组件化的问题，提供了 redux 之上的 component 的封装，并创新的通过这一层的架构设计提供了业务代码分治的能力。第三版 完成了 Redux， Component 两层的设计，其中包含了 Connector，Dependencies，Context 等重要概念。\n\n    -   3.1 解决集中和分治的矛盾的核心在于 [Connector](what's-connector.md)\n    -   3.2 这一层的组件的分治是面向通用设计的。通过在 [Dependencies](dependencies-cn.md) 配置 slots，得到了可插拔的组件系统。\n\n-   4. 在第三个版本 Redux & Component 之外，提供了面向 ListView 场景的分治设计 Adapter。\n    -   解决了在面向 ListView 场景下的逻辑的分治和性能降低的矛盾。\n    -   [what's-adapter](what's-adapter.md)\n\n> 目前，fish redux 已经在闲鱼线上稳定运行，未来，期待 fish redux 给社区带来更多的输入。\n"
  },
  {
    "path": "doc/concept/features.md",
    "content": "# Features\n\n## 直接使用 flutter 会面临的问题？\n\n> [flutter](https://github.com/flutter/flutter) 是 google 推出的新一代跨平台渲染框架.\n> 它帮助开发者解决了跨平台，高性能，富有表现力和灵活的 UI 表达，快速开发等核心问题。\n> 但是如果开发大应用，还需要解决以下问题。\n>\n> > 1. 数据流问题\n> > 2. 通信问题\n> > 3. 可插拔的组件系统\n> > 4. 展示和逻辑解耦\n> > 5. 统一的编程模型和规范\n>\n> 我们可以类比 flutter 和 React，事实上在中大型应用中 React 会面临的绝大多数问题，flutter 也同样面临考验。\n\n## 数据流问题\n\n> 目前社区流行的数据流方案有：\n> 单向数据流方案，以 Redux 为代表\n> 响应式数据流方案，以 Mobx 为代表\n> 其他，以 rxjs 为代表\n> 那么哪一种架构最合适 flutter ？\n> 我们追随了 javascript 栈绝大多数开发者的选择 - [ReduxJs](https://github.com/reduxjs/redux)\n> 感谢 ReduxJs，我们是几乎 100%的还原了它在 dart 上的实现。所以我们也继承了它的优点：[Predictable],[Centralized],[Debuggable],[Flexible]。\n\n## 通信问题\n\n> 直接使用 flutter，在 Widgets 之间传递状态和回调，随着应用复杂度的上升，会变成是一件可怕而糟糕的事情。\n> 通过 fish redux，依托于集中的 Redux 和分治的 Effect 模块，通过一个极简的 [dispatch-api](mechanism.md)，完成所有的通信的诉求。\n\n## 可插拔的组件系统\n\n> fish redux 通过一个配置式的 Dependencies，来完成灵活的可插拔的组件系统。同时有这一配置的存在，它解放了我们手动拼装 Reducer 的繁琐工作。\n> 参考:\n>\n> > 1. [what's-connector](what's-connector.md)\n> > 2. [connector](connector.md)\n> > 3. [dependencies](dependencies.md)\n> > 4. [component](component.md)\n> > 5. [adapter](adapter.md)\n> > 6. [what's-adapter](what's-adapter.md)\n\n## 展示和逻辑解耦\n\n> fish redux 从 [elm](https://guide.elm-lang.org/) 中得到了非常多的设计灵感。\n> 将一个组件，拆分为相互独立的 View，Effect，Reducer 三个函数，我们优雅的解决了展示和逻辑解耦的问题。\n> 通过这样的拆分，我们将 UI 的表达隔离于一个函数内，它让我们更好的面向未来，一份 UI 表达它可能来自于开发者，可能来自于深度学习框架的 UI 代码生成，可能是面向移动终端，也可能是面向浏览器。它让我们有了更多的组合的可能。\n> 同时函数式的编程模型带来了更容易编写，更容易扩展，更容易测试，更容易维护等特性。\n\n## 统一的编程模型和规范\n\n> [directory](directory.md)\n"
  },
  {
    "path": "doc/concept/filter-cn.md",
    "content": "# Filter\n\n-   Filter 是用来优化 Reducer 的性能的。因为 Reducer 是层层组装的，所以处理每一个 Action，理论上会遍历一遍所有的小 Reducer，在一些非常复杂的场景下，这样的一次深度遍历的耗时可能会到毫秒级别（一般情况下都应该小于 1 毫秒）。那么我们需要对 Reducer 做性能优化，提前决定要不要遍历这份 Reducer 子树，减少遍历的深度和次数。\n-   示例代码\n\n```dart\nbool filter(Action action) {\n    return action.type == 'some action';\n}\n```\n"
  },
  {
    "path": "doc/concept/filter.md",
    "content": "# Filter\n\n-   Filter is used to optimize the performance of the Reducer. Because the Reducer is layer-assembled, each Action is processed, and in theory, all the small Reducers are traversed. In some very complicated scenarios, such a deep traversal may take up to the millisecond level (generally Should be less than 1 millisecond). Then we need to optimize the performance of the Reducer, decide in advance whether to traverse this Reducer subtree, reduce the depth and number of traversal.\n-   Sample Code\n\n```dart\nbool filter(Action action) {\n    return action.type == 'some action';\n}\n```\n"
  },
  {
    "path": "doc/concept/higher-effect-cn.md",
    "content": "# HigherEffect\n\n-   由于 Effect 有可能有自己一些临时状态（尽管不建议这么做，但还是提供了支持），为了支持这个特性，我们将 Effect 提升为高阶函数，将它的状态放在闭包里。\n-   框架支持 Effect|HigherEffect 的配置，但是不能对一个组件或适配器同时都配置，那样会带来困扰，一般情况下，都配置往往是个显式的疏忽大意。\n-   HigherEffect = (Context ctx) => (Action action) => FutureOr\n-   更详细的例子请参考 [OOP](oop-cn.md) - EffectPart\n"
  },
  {
    "path": "doc/concept/higher-effect.md",
    "content": "# HigherEffect\n\n-   Since Effect may have some temporary state of its own (although it is not recommended, support is provided), in order to support this feature, we promote the Effect to a higher-order function and put its state in the closure.\n-   The framework supports the configuration of Effect|HigherEffect on component or adapter, but it can't be configured for one component or adapter at the same time, which will cause trouble. In general, the configuration is often an explicit negligence.\n-   HigherEffect = (Context ctx) => (Action action) => FutureOr\n-   For more detailed examples, please refer to [OOP](oop.md) - EffectPart\n"
  },
  {
    "path": "doc/concept/lifecycle-cn.md",
    "content": "# Lifecycle\n\n-   默认的所有生命周期，本质上都来自于 flutter State<StatefulWidget> 中的生命周期。\n    -   initState\n    -   didChangeDependencies\n    -   build\n    -   didUpdateWidget\n    -   deactivate\n    -   dispose\n-   在组件内，Reducer 的生命周期是和页面一致的，Effect 和 View 的生命周期是和组件的 Widget 一致的。\n-   在适配器中，Reducer 的生命周期是和页面一致的，Effect 的生命周期是和 ListView 的生命周期一致，View 的生命周期是短暂的(划入不可见区域即销毁)。同时增加了 appear 和 disappear 的生命周期， 代表这个 adapter 管理的视图数组，刚进入显示区和完全离开显示区的回调。\n"
  },
  {
    "path": "doc/concept/lifecycle.md",
    "content": "# Lifecycle\n\n-   The default all lifecycles are essentially derived from the lifecycle in flutter State<StatefulWidget>.\n    -   initState\n    -   didChangeDependencies\n    -   build\n    -   didUpdateWidget\n    -   deactivate\n    -   dispose\n-   Within the component, the Lifecycle of the Reducer is consistent with the page, and the lifecycle of Effect and View is consistent with the component's Widget.\n-   In the adapter, the Lifecycle of the Reducer is consistent with the page. The life cycle of the Effect is the same as the life cycle of the ListView. The life cycle of the View is short-lived (destroyed in the invisible area). At the same time, the life cycle of appear and disappear is added, representing the view array managed by this adapter, the callback just entering the display area and completely leaving the display area.\n"
  },
  {
    "path": "doc/concept/mechanism-cn.md",
    "content": "# Communication Mechanism\n\n## 页面内通信\n\n-   组件|适配器内通信\n-   组件|适配器间内通信\n\n![image.png | left | 747x399](https://cdn.nlark.com/lark/0/2018/png/82574/1545365233153-4c8105b4-050c-49e6-be02-dbf28a861caa.png)\n\nSelf-First-Broadcast。\n发出的 Action，自己优先处理，否则广播给其他组件和 Redux 处理。\n\n最终我们通过一个简单而直观的 dispatch 完成了组件内，组件间（父到子，子到父，兄弟间等）的通信。\n\n## 页面间通信\n\n-   页面间通信\n    -   Context.appBroadcast\n        -   每一个页面的 PageStore 都会收到消息，各自独立负责处理。\n\n![image.png | left | 691x519](https://cdn.nlark.com/lark/0/2018/png/82574/1545368705599-745c46a3-f5c6-41a7-a757-1bc6f9a389d4.png)\n\n# Refresh Mechanism\n\n## 数据刷新\n\n-   局部数据修改，自动层层触发上层数据的浅拷贝，对业务代码是透明的。\n-   层层的数据的拷贝\n    -   一方面是对 Redux 数据修改的严格的 follow。\n    -   另一方面也是对数据驱动展示的严格的 follow。\n        -   数据的任何一个局部的变动，必须要让能看到这个局部的所有视图感知到。如果不拷贝，对应的视图通过新旧两份数据的比较（同一个引用），会错以为自己没有发生变化。\n\n![image.png | left | 747x361](https://cdn.nlark.com/lark/0/2018/png/82574/1545386668521-0081cb5f-8017-47d1-ad7c-8802bb0be8a0.png)\n\n## 视图刷新\n\n-   扁平化通知到所有组件，组件通过 shouldUpdate 确定自己是否需要刷新\n\n![image.png | left | 747x336](https://cdn.nlark.com/lark/0/2018/png/82574/1545386773247-2eddfa99-e6b9-4be9-ac43-d1944ff44e9b.png)\n"
  },
  {
    "path": "doc/concept/mechanism.md",
    "content": "# Communication Mechanism\n\n## Page internal communication\n\n-   Component internal communication\n-   Inter-component communication\n\n![image.png | left | 747x399](https://cdn.nlark.com/lark/0/2018/png/82574/1545365233153-4c8105b4-050c-49e6-be02-dbf28a861caa.png)\n\nSelf-First-Broadcast。\nThe emitted Action will be processed first by its own Effect, otherwise it will be broadcast to other components and Redux.\n\nWe completed the communication between the components (parent to child, child to parent, brother, etc.) through a simple and intuitive dispatch.\n\n## Inter-page communication\n\n-   Context.appBroadcast\n    -   Each page's PageStore receives an action which is handled independently.\n\n![image.png | left | 691x519](https://cdn.nlark.com/lark/0/2018/png/82574/1545368705599-745c46a3-f5c6-41a7-a757-1bc6f9a389d4.png)\n\n# Refresh Mechanism\n\n## 数据刷新\n\n-   Local data modification automatically triggers a shallow copy of the upper layer data and is transparent to the business code.\n\n![image.png | left | 747x361](https://cdn.nlark.com/lark/0/2018/png/82574/1545386668521-0081cb5f-8017-47d1-ad7c-8802bb0be8a0.png)\n\n## View refresh\n\n-   When the state changes, the store flatly notifies all the components and the [ShouldUpdate](should-update.md) decide whether the view should be refreshed\n\n![image.png | left | 747x336](https://cdn.nlark.com/lark/0/2018/png/82574/1545386773247-2eddfa99-e6b9-4be9-ac43-d1944ff44e9b.png)\n"
  },
  {
    "path": "doc/concept/middleware-cn.md",
    "content": "# Middleware\n\n关于 Middleware 的定义、签名和 ReduxJS 社区是一致的。\n\n示例代码\n\n```dart\nMiddleware<T> logMiddleware<T>({\n  String tag = 'redux',\n  String Function(T) monitor,\n}) {\n  return ({Dispatch dispatch, Get<T> getState}) {\n    return (Dispatch next) {\n      return isDebug()\n          ? (Action action) {\n              print('---------- [$tag] ----------');\n              print('[$tag] ${action.type} ${action.payload}');\n\n              final T prevState = getState();\n              if (monitor != null) {\n                print('[$tag] prev-state: ${monitor(prevState)}');\n              }\n\n              next(action);\n\n              final T nextState = getState();\n              if (monitor != null) {\n                print('[$tag] next-state: ${monitor(nextState)}');\n              }\n\n              if (prevState == nextState) {\n                print('[$tag] warning: ${action.type} has not been used.');\n              }\n\n              print('========== [$tag] ================');\n            }\n          : next;\n    };\n  };\n}\n```\n\n更多的参考 src/utils/common_middleware\n"
  },
  {
    "path": "doc/concept/middleware.md",
    "content": "# Middleware\n\n-   The definition and signature of Middleware is consistent with the ReduxJS community.\n-   Sample Code\n\n```dart\nMiddleware<T> logMiddleware<T>({\n  String tag = 'redux',\n  String Function(T) monitor,\n}) {\n  return ({Dispatch dispatch, Get<T> getState}) {\n    return (Dispatch next) {\n      return isDebug()\n          ? (Action action) {\n              print('---------- [$tag] ----------');\n              print('[$tag] ${action.type} ${action.payload}');\n\n              final T prevState = getState();\n              if (monitor != null) {\n                print('[$tag] prev-state: ${monitor(prevState)}');\n              }\n\n              next(action);\n\n              final T nextState = getState();\n              if (monitor != null) {\n                print('[$tag] next-state: ${monitor(nextState)}');\n              }\n\n              if (prevState == nextState) {\n                print('[$tag] warning: ${action.type} has not been used.');\n              }\n\n              print('========== [$tag] ================');\n            }\n          : next;\n    };\n  };\n}\n```\n\n更多的参考 src/utils/common_middleware\n"
  },
  {
    "path": "doc/concept/on-error-cn.md",
    "content": "# OnError\n\n-   集中处理由 Effect 产生的业务异常，无论是同步函数还是异步函数。有了统一的异常处理机制，我们就能站在一个更高的抽象角度，对业务代码做出合理的简化。\n-   示例代码\n\n```dart\nbool onMessageError(Exception e, Context<String> ctx) {\n    if(e is BizException) {\n        ///do some toast\n        return true;\n    }\n    return false;\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n            reducer: buildMessageReducer(),\n            onError: onMessageError,\n        );\n}\n```\n"
  },
  {
    "path": "doc/concept/on-error.md",
    "content": "# OnError\n\n-   Centralizes the business exceptions generated by Effect, whether it is a synchronous function or an asynchronous function. With a unified exception handling mechanism, we can stand on a higher level of abstraction and make reasonable simplifications of business code.\n-   Sample Code\n\n```dart\nbool onMessageError(Exception e, Context<String> ctx) {\n    if(e is BizException) {\n        ///do some toast\n        return true;\n    }\n    return false;\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n            reducer: buildMessageReducer(),\n            onError: onMessageError,\n        );\n}\n```\n"
  },
  {
    "path": "doc/concept/oop-cn.md",
    "content": "# OOP\n\n-   虽然框架推荐使用的函数式的编程方式，也提供面向对象式的编程方式的支持。\n    -   ViewPart\n        -   需要复写 build 函数。\n        -   需要的 state，dispatch，viewService 的参数，已经成为了对象的字段可以直接使用。\n        -   它是@immutable 的，所以不应该也不需要在内部定义可变字段。\n    -   EffectPart\n        -   需要复写 createMap 函数。\n        -   需要的 Context 已经被打平，作为了对象的字段可以直接使用。\n        -   可以定义字段，它的可见性也仅限于自身。\n        -   它必须配合 higherEffect 一起使用。\n-   示例代码\n\n```dart\nclass MessageView extends ViewPart<MessageState> {\n    @override\n    Widget build() {\n        return Column(children: [\n            viewService.buildComponent('profile'),\n            InkWell(\n                child: Text('$message'),\n                onTap: () => dispatch(const Action('onShare')),\n            ),\n        ]);\n    }\n}\n\nclass MessageEffect extends EffectPart<MessageState> {\n    ///we could put some Non-UI fields here.\n\n    @override\n    Map<Object, OnAction> createMap() {\n        return <Object, OnAction>{\n            Lifecycle.initState: _initState,\n            'onShare': _onShare,\n        };\n    }\n\n    void _initState(Action action) {\n        //do something on initState\n    }\n\n    void _onShare(Action action) async {\n        //do something on onShare\n        await Future<void>.delayed(Duration(milliseconds: 1000));\n        dispatch(const Action('shared'));\n    }\n}\n\nclass MessageComponent extends Component<MessageState> {\n    MessageComponent(): super(\n        view: MessageView().asView(),\n        higherEffect: higherEffect(() => MessageEffect()),\n    );\n}\n```\n"
  },
  {
    "path": "doc/concept/oop.md",
    "content": "# OOP\n\n-   Although the framework recommends the use of functional programming, it also provides object-oriented programming support.\n    -   ViewPart\n        -   Need to override the 'build' function.\n        -   The required state, dispatch, and viewService parameters have become fields of the object and can be used directly.\n        -   It is immutable, so there should be no need to define variable fields internally.\n    -   EffectPart\n        -   Need to override the 'createMap' function.\n        -   The required Context has been flattened as the fields which can be used directly.\n        -   Fields can be defined and their visibility is limited to themselves.\n        -   It must be used with higherEffect.\n-   Sample Code\n\n```dart\nclass MessageView extends ViewPart<MessageState> {\n    @override\n    Widget build() {\n        return Column(children: [\n            viewService.buildComponent('profile'),\n            InkWell(\n                child: Text('$message'),\n                onTap: () => dispatch(const Action('onShare')),\n            ),\n        ]);\n    }\n}\n\nclass MessageEffect extends EffectPart<MessageState> {\n    ///we could put some Non-UI fields here.\n\n    @override\n    Map<Object, OnAction> createMap() {\n        return <Object, OnAction>{\n            Lifecycle.initState: _initState,\n            'onShare': _onShare,\n        };\n    }\n\n    void _initState(Action action) {\n        //do something on initState\n    }\n\n    void _onShare(Action action) async {\n        //do something on onShare\n        await Future<void>.delayed(Duration(milliseconds: 1000));\n        dispatch(const Action('shared'));\n    }\n}\n\nclass MessageComponent extends Component<MessageState> {\n    MessageComponent(): super(\n        view: MessageView().asView(),\n        higherEffect: higherEffect(() => MessageEffect()),\n    );\n}\n```\n"
  },
  {
    "path": "doc/concept/page-cn.md",
    "content": "# Page\n\n-   一个页面内都有且仅有一个 Store\n-   Page 继承于 Component，所以它能配置所有 Component 的要素\n-   Page 能配置 Middleware，用于对 Redux 做 AOP 管理\n-   Page 必须配置一个初始化页面数据的初始化函数  initState\n    <img src=\"https://img.alicdn.com/tfs/TB1ASfDJ9zqK1RjSZFHXXb3CpXa-1636-756.png\" width=\"818px\" height=\"378px\">\n\n-   示例代码\n\n```dart\n/// Hello World\nclass HelloWordPage extends Page<String, String> {\n    HelloWordPage():\n        super(\n            initState: (String msg) => msg,\n            view:(String msg, _, __) => Text('Hello ${msg}'),\n        );\n}\n\nHelloWordPage().buildPage('world')\n```\n"
  },
  {
    "path": "doc/concept/page.md",
    "content": "# Page\n\n-   One and only one store in one page\n-   Page inherits from Component, so it can configure all the factors of Component.\n-   Page can configure Middleware for AOP management of Redux.\n-   Page must be configured with an initialization function that initializes page data initState.\n    <img src=\"https://img.alicdn.com/tfs/TB1ASfDJ9zqK1RjSZFHXXb3CpXa-1636-756.png\" width=\"818px\" height=\"378px\">\n\n-   Sample Code\n\n```dart\n/// Hello World\nclass HelloWordPage extends Page<String, String> {\n    HelloWordPage():\n        super(\n            initState: (String msg) => msg,\n            view:(String msg, _, __) => Text('Hello ${msg}'),\n        );\n}\n\nHelloWordPage().buildPage('world')\n```\n"
  },
  {
    "path": "doc/concept/reducer-cn.md",
    "content": "# Reducer\n\n-   Reducer 是一个上下文无关的 pure function。它接收下面的参数\n    -   T state\n    -   Action action\n-   它主要包含三方面的信息\n    -   接收一个“意图”， 做出数据修改。\n    -   如果要修改数据，需要创建一份新的拷贝，修改在拷贝上。\n    -   如果数据修改了，它会自动触发 State 的层层数据的拷贝，再以扁平化方式通知组件刷新。\n-   示例代码\n\n```dart\n/// one style of writing\nString messageReducer(String msg, Action action) {\n  if (action.type == 'shared') {\n    return '$msg [shared]';\n  }\n  return msg;\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n            reducer: messageReducer,\n        );\n}\n```\n\n```dart\n/// another style of writing\nReducer<String> buildMessageReducer() {\n  return asReducer(<Object, Reducer<String>>{\n    'shared': _shared,\n  });\n}\n\nString _shared(String msg, Action action) {\n  return '$msg [shared]';\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n            reducer: buildMessageReducer(),\n        );\n}\n```\n\n> 推荐的是第二种写法\n"
  },
  {
    "path": "doc/concept/reducer.md",
    "content": "# Reducer\n\n-   The Reducer is a context-independent pure function. It receives the following parameters\n    -   T state\n    -   Action action\n-   It mainly contains three aspects of information\n    -   Receive an \"intent\" and make a state modification.\n    -   If you want to modify the state, you need to create a new copy and modify it on the copy.\n    -   If the small state is modified, it will automatically trigger the copy of the main state's layers data, and then notify the components to refresh in a flattened manner.\n-   Sample Code\n\n```dart\n/// one style of writing\nString messageReducer(String msg, Action action) {\n  if (action.type == 'shared') {\n    return '$msg [shared]';\n  }\n  return msg;\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n            reducer: messageReducer,\n        );\n}\n```\n\n```dart\n/// another style of writing\nReducer<String> buildMessageReducer() {\n  return asReducer(<Object, Reducer<String>>{\n    'shared': _shared,\n  });\n}\n\nString _shared(String msg, Action action) {\n  return '$msg [shared]';\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n            reducer: buildMessageReducer(),\n        );\n}\n```\n\n> 推荐的是第二种写法\n"
  },
  {
    "path": "doc/concept/redux-cn.md",
    "content": "### Redux\n\n-   State\n-   [Action](action-cn.md)\n-   [Reducer](reducer-cn.md)\n-   Store\n-   [Middleware](middleware-cn.md)\n\n以上概念和社区的 Redux 是完全一致的。\nRedux 是一个用来做[可预测][集中式][易调试][灵活性]的数据管理的框架。\n如果想对 Redux 有更近一步的理解，请参考 [https://github.com/reduxjs/redux](https://github.com/reduxjs/redux)\n"
  },
  {
    "path": "doc/concept/redux.md",
    "content": "### Redux\n\n-   State\n-   [Action](action.md)\n-   [Reducer](reducer.md)\n-   Store\n-   [Middleware](middleware.md)\n    The above concepts are exactly the same as the community's Redux.\n    Redux is a framework for state management with [predictable][centralized] [easy to debug][flexibility].\n    If you want to have a closer understanding of Redux, please refer to [https://github.com/reduxjs/redux](https://github.com/reduxjs/redux)\n"
  },
  {
    "path": "doc/concept/should-update-cn.md",
    "content": "# ShouldUpdate\n\n-   当数据发生变更，Store 扁平化地通知所有组件\n-   框架默认使用 identical 比较新旧两份数据来决定是否需要刷新。\n-   如果我们对组件的刷新会有非常精确化的诉求， 那么我们可以自己定义一个 ShouldUpdate。\n-   示例代码\n\n```dart\nbool shouldUpdate(DetailState old, DetailState now) {\n    return old.message != now.message;\n}\n```\n"
  },
  {
    "path": "doc/concept/should-update.md",
    "content": "# ShouldUpdate\n\n-   When the state changes, the store flatly notifies all the components.\n-   By default, the framework uses identical to compare the old and new state to determine if a refresh is needed.\n-   If we have a very precise request for component refresh, then we can define a ShouldUpdate ourselves.\n-   Sample Code\n\n```dart\nbool shouldUpdate(DetailState old, DetailState now) {\n    return old.message != now.message;\n}\n```\n"
  },
  {
    "path": "doc/concept/static-flow-adapter-cn.md",
    "content": "# StaticFlowAdapter\n\n-   模版是一个 Array，接受 Object|Map 的数据驱动。\n-   模版接收一个 Dependent 的数组，每一个 Dependent 可以是 Component 或者 Adapter + Connector<T,P> 的组合。\n-   抽象地看，它非常的像是一个 flatMap + compact 的操作。\n-   示例代码\n\n```dart\nclass ItemBodyComponent extends Component<ItemBodyState> {\n    ItemBodyComponent()\n        : super(\n            view: buildItemBody,\n            dependencies: Dependencies<ItemBodyState>(\n            adapter: StaticFlowAdapter<ItemBodyState>(\n                slots: <Dependent<ItemBodyState>>[\n                    VideoAdapter().asDependent(videoConnector()),\n                    UserInfoComponent().asDependent(userInfoConnector()),\n                    DescComponent().asDependent(descConnector()),\n                    ItemImageComponent().asDependent(itemImageConnector()),\n                    OriginDescComponent().asDependent(originDescConnector()),\n                    VisitComponent().asDependent(visitConnector()),\n                    SameMoreComponent().asDependent(sameMoreConnector()),\n                    PondComponent().asDependent(pondConnector()),\n                    CommentAdapter().asDependent(commentConnector()),\n                    RecommendAdapter().asDependent(recommendConnector()),\n                    PaddingComponent().asDependent(paddingConnector()),\n                ]),\n            ),\n        );\n}\n\n```\n\n<img src=\"https://img.alicdn.com/tfs/TB1sXXOLQvoK1RjSZPfXXXPKFXa-1666-1104.png\" width=\"833px\" height=\"552px\">\n"
  },
  {
    "path": "doc/concept/static-flow-adapter.md",
    "content": "# StaticFlowAdapter\n\n-   The template is an Array that accepts map-like data driven.\n-   The template receives an array of Dependents.\n-   It's similar with a flatMap + compact operation abstractly.\n-   Sample Code\n\n```dart\nclass ItemBodyComponent extends Component<ItemBodyState> {\n    ItemBodyComponent()\n        : super(\n            view: buildItemBody,\n            dependencies: Dependencies<ItemBodyState>(\n            adapter: StaticFlowAdapter<ItemBodyState>(\n                slots: <Dependent<ItemBodyState>>[\n                    VideoAdapter().asDependent(videoConnector()),\n                    UserInfoComponent().asDependent(userInfoConnector()),\n                    DescComponent().asDependent(descConnector()),\n                    ItemImageComponent().asDependent(itemImageConnector()),\n                    OriginDescComponent().asDependent(originDescConnector()),\n                    VisitComponent().asDependent(visitConnector()),\n                    SameMoreComponent().asDependent(sameMoreConnector()),\n                    PondComponent().asDependent(pondConnector()),\n                    CommentAdapter().asDependent(commentConnector()),\n                    RecommendAdapter().asDependent(recommendConnector()),\n                    PaddingComponent().asDependent(paddingConnector()),\n                ]),\n            ),\n        );\n}\n\n```\n\n<img src=\"https://img.alicdn.com/tfs/TB1sXXOLQvoK1RjSZPfXXXPKFXa-1666-1104.png\" width=\"833px\" height=\"552px\">\n"
  },
  {
    "path": "doc/concept/view-cn.md",
    "content": "# View\n\n-   View 是一个输出 Widget 的上下文无关的函数。它接收下面的参数\n    -   T state\n    -   Dispatch\n    -   ViewService\n-   它主要包含三方面的信息\n    -   视图完全由数据驱动。\n    -   视图产生的事件／回调，通过 Dispatch 发出“意图”，但绝不做具体的实现。\n    -   使用依赖的组件／适配器，通过在组件上显示配置，再通过 ViewService 标准化调用。\n        -   其中 ViewService 提供了三个能力\n            -   BuildContext context，获取 flutter Build-Context 的能力\n            -   Widget buildView(String name), 直接创建子组件的能力\n                -   这里传入的 name 即在 Dependencies 上配置的名称。\n                -   创建子组件不需要传入任何其他的参数，因为子组件需要的参数，已经通过 Dependencies 配置中，将它们的数据关系，通过 connector 确立。\n            -   ListAdapter buildAdapter()， 直接创建适配器的能力\n-   示例代码\n\n```dart\nWidget buildMessageView(String message, Dispatch dispatch, ViewService viewService) {\n  return Column(children: [\n    viewService.buildComponent('profile'),\n    InkWell(\n      child: Text('$message'),\n      onTap: () => dispatch(const Action('onShare')),\n    ),\n  ]);\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n        );\n}\n```\n"
  },
  {
    "path": "doc/concept/view.md",
    "content": "# View\n\n-   View is a context-independent function that outputs Widget. It receives the following parameters\n    -   T state\n    -   Dispatch\n    -   ViewService\n-   It mainly contains three aspects of information\n    -   The view is completely driven by data.\n    -   The event/callback triggered by the view, use Dispatch to send \"intent\", but never do a specific implementation.\n    -   Use dependent component/adapter, by explicitly configuring it on the parent component, and then standardizing calls through the ViewService.\n        -   Where ViewService provides three capabilities\n            -   BuildContext context: Ability to get widget's BuildContext\n            -   Widget buildView(String name): Ability to create subcomponents directly\n                -   The name passed in here is the name configured on Dependencies.\n                -   Creating subcomponents does not require passing in any other parameters, since the parameters required by the subcomponents have been passed through the Dependencies configuration, and their data relationships are established via the connector.\n            -   ListAdapter buildAdapter(): Ability to create adapter directly\n-   Sample Code\n\n```dart\nWidget buildMessageView(String message, Dispatch dispatch, ViewService viewService) {\n  return Column(children: [\n    viewService.buildComponent('profile'),\n    InkWell(\n      child: Text('$message'),\n      onTap: () => dispatch(const Action('onShare')),\n    ),\n  ]);\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n        );\n}\n```\n"
  },
  {
    "path": "doc/concept/what's-adapter.md",
    "content": "# What's adapter\n\n面向 ListView 场景的分治设计 Adapter。\n\n> 在解答什么是 adapter 之前，我们来看下一般框架对 ListView 的分治是怎么做的。传统的手段，我们对 ListView 的分治更多的局限于它展现部分，而它的逻辑部分往往是集中的。而当我们试图将 ListView 下的某一局部的展现和逻辑封装在一起，我们就会遇到\"Big-Cell\"问题，面临性能的显著降低。\n> 这里面存在一个分治和性能上的矛盾。这个矛盾带来了复用难，可维护差，难以协作等中大型场景下的问题。\n>\n> 解决这个问题，有两种思路：\n>\n> 1. 下沉到 UI 表达层（Widgets），去实现一个高性能的 ScrollView。\n> 2. 向上做模型抽象，得到一个逻辑上的 ScrollView，性能上的 ListView。\n>\n> fish redux 选择了第二条更加通用的路径来解决 LisView 下的分治问题。\n>\n> 一个 ListView 对应了一个 Adapter，这看上去非常的像 Android 里的设计，但事实上 fish-redux 里的 Adapter 概念走的更远。\n>\n> 1. 一个 Adapter 是可以由多个 Component 和 Adapter 组合而成，它有点像 flatmap & compact 的 api 的叠加。\n> 2. Adapter 以及它的子 Adapter 的生命周期是和 ListView 是等效的。它像跨斗一般附着于 ListView 的生命周期之上。同时由于 Adapter 生命周期的提升，我们额外收获了两个非常有用的事件消息(appear & disappear)。\n>\n> > 注意 ⚠️ 在 Adapter 里配置的子 Component，它的生命周期和它所对应的 WidgetState 是一致的，所以它的是短暂的。\n\n-   Adapter 的容器有两类，用图来说明吧：\n\n<img src=\"https://img.alicdn.com/tfs/TB1sXXOLQvoK1RjSZPfXXXPKFXa-1666-1104.png\" width=\"833px\" height=\"552px\">\n\n<img src=\"https://img.alicdn.com/tfs/TB10lxHLMDqK1RjSZSyXXaxEVXa-1838-1024.png\" width=\"919px\" height=\"512px\">\n"
  },
  {
    "path": "doc/concept/what's-connector.md",
    "content": "# What's connector\n\n在解答 connector 是什么之前，我们来先看一个代码片段\n\n```javascript\nlet hasChanged = false;\nconst nextState = {};\nfor (let i = 0; i < finalReducerKeys.length; i++) {\n\tconst key = finalReducerKeys[i];\n\tconst reducer = finalReducers[key];\n\tconst previousStateForKey = state[key];\n\tconst nextStateForKey = reducer(previousStateForKey, action);\n\tif (typeof nextStateForKey === 'undefined') {\n\t\tconst errorMessage = getUndefinedStateErrorMessage(key, action);\n\t\tthrow new Error(errorMessage);\n\t}\n\tnextState[key] = nextStateForKey;\n\thasChanged = hasChanged || nextStateForKey !== previousStateForKey;\n}\nreturn hasChanged ? nextState : state;\n```\n\n以上来自于 Reduxjs-[combineReducers](https://github.com/reduxjs/redux/blob/master/src/combineReducers.js)的核心实现。\n\ncombineReducers 是一个将 Reducer 分治的函数，让一个庞大数据的 Reducer 可以由多层的更小的 Reducer 组合而成。\n\n这是 Redux 框架里的核心 API，但是他有缺点。他有非常明显的语言的局限性，如下 3 点:\n\n1. 浅拷贝一个任意对象\n\n```javascript\nconst nextState = {};\n```\n\n2. 读取字段\n\n```javascript\nconst previousStateForKey = state[key];\n```\n\n3. 写入字段\n\n```javascript\nnextState[key] = nextStateForKey;\n```\n\n将上面的 3 点抽象来看：\n\n1. State 的 clone 的能力（浅拷贝）\n2. Get & Set 的能力，即为 Connector 的概念。\n\n有了 以上两点，我们才完全集成了 Redux 的所有精华，同时将它的设计更上一个通用的维度。\n"
  },
  {
    "path": "doc/concept/what's-the-diiference-cn.md",
    "content": "# What's different with Redux ?\n\n## 它们是解决不同层面问题的两个框架\n\n> Redux 是一个专注于状态管理的框架；Fish Redux 是基于 Redux 做状态管理的应用框架。\n\n> 应用框架不仅仅要解决状态管理的问题，还要解决分治，通信，数据驱动，解耦等等问题。\n\n## Fish Redux 解决了集中和分治的矛盾。\n\n> Redux 通过使用者手动组织代码的形式来完成从小的 Reducer 到主 Reducer 的合并过程；\n\n> Fish Redux 通过显式的表达组件之间的依赖关系，由框架自动完成从细力度的 Reducer 到主 Reducer 的合并过程；\n\n<img src=\"https://img.alicdn.com/tfs/TB1oeXKJYPpK1RjSZFFXXa5PpXa-1976-568.png\" width=\"988px\" height=\"284px\">\n\n## Fish Redux 提供了一个简单的组件抽象模型\n\n> 它通过简单的 3 个函数组合而成\n\n<img src=\"https://img.alicdn.com/tfs/TB1vqB2J4YaK1RjSZFnXXa80pXa-900-780.png\" width=\"450px\" height=\"390px\">\n\n## Fish Redux 提供了一个 Adapter 的抽象组件模型\n\n> 在基础的组件模型以外，Fish Redux 提供了一个 Adapter 抽象模型，用来解决在 ListView 上大 Cell 的性能问题。\n\n> 通过上层抽象，我们得到了逻辑上的 ScrollView，性能上的 ListView。\n\n<img src=\"https://img.alicdn.com/tfs/TB1x51VJ7PoK1RjSZKbXXX1IXXa-1852-612.png\" width=\"617px\" height=\"204px\">\n"
  },
  {
    "path": "doc/concept/what's-the-diiference.md",
    "content": "# What's different with Redux ?\n\n## They are two frameworks for solving problems at different layers.\n\n> Redux is a framework focused on state management; Fish Redux is an application framework based on Redux for state management.\n\n> The application framework not only solves the problem of state management, but also solves the problems of divide and conquer, communication, data drive, decoupling and so on.\n\n## Fish Redux solves the contradiction between concentration and division.\n\n> Redux completes the merge process from the small Reducers to the main Reducer by the user manually organizing the code;\n\n> Fish Redux automatically completes the merge process from the small Reducers to the main Reducer by explicitly expressing the dependencies between components;\n\n<img src=\"https://img.alicdn.com/tfs/TB1oeXKJYPpK1RjSZFFXXa5PpXa-1976-568.png\" width=\"988px\" height=\"284px\">\n\n## Fish Redux provides a simple component abstract model\n\n> It is a combination of simple 3 functions\n\n<img src=\"https://img.alicdn.com/tfs/TB1vqB2J4YaK1RjSZFnXXa80pXa-900-780.png\" width=\"450px\" height=\"390px\">\n\n## Fish Redux provides an abstract component model of the Adapter\n\n> In addition to the underlying component model, Fish Redux provides an Adapter abstraction model to solve the performance problems of large cells on ListView.\n\n> Through the upper abstraction, we get the logical ScrollView, the performance of the ListView.\n\n<img src=\"https://img.alicdn.com/tfs/TB1x51VJ7PoK1RjSZKbXXX1IXXa-1852-612.png\" width=\"617px\" height=\"204px\">\n"
  },
  {
    "path": "doc/concept/widget-wrapper-cn.md",
    "content": "### WidgetWrapper\n\n-   它用来解决 flutter 的 ui 体系下，一些需要实现特色接口的 Widget，比如 KeepAlive，因为通过 Component 产生的 Widget 会被一个框架内部的 Stateful 的 Widget 所包裹。\n-   示例代码\n\n```dart\nimport 'package:flutter/material.dart' hide Action, Page;\n\nWidget repaintBoundaryWrapper(Widget widget) {\n  return RepaintBoundary(child: widget);\n}\n```\n"
  },
  {
    "path": "doc/concept/widget-wrapper.md",
    "content": "### WidgetWrapper\n\n-   It is used to solve flutter's ui system, some Widgets that need to implement the featured interface, such as KeepAlive, because the Widget generated by Component will be wrapped by a Stateful Widget inside the fish redux framework.\n-   Sample Code\n\n```dart\nimport 'package:flutter/material.dart' hide Action, Page;\n\nWidget repaintBoundaryWrapper(Widget widget) {\n  return RepaintBoundary(child: widget);\n}\n```\n"
  },
  {
    "path": "doc/introduction/README-cn.md",
    "content": "# 简介\n\nFish Redux 是一个基于 Redux 数据管理的组装式 flutter 应用框架， 它特别适用于构建中大型的复杂应用。\n\n它的特点是配置式组装。\n一方面我们将一个大的页面，对视图和数据层层拆解为互相独立的 Component|Adapter，上层负责组装，下层负责实现；\n另一方面将 Component|Adapter 拆分为 View，Reducer，Effect 等相互独立的上下文无关函数。\n\n所以它会非常干净，易维护，易协作。\n\nFish Redux 的灵感主要来自于 Redux， Elm， Dva 这样的优秀框架。而 Fish Redux 站在巨人的肩膀上，将集中，分治，复用，隔离做的更进一步。\n"
  },
  {
    "path": "doc/introduction/README.md",
    "content": "# Introduction\n\nFish Redux is an assembled flutter application framework based on Redux state management, it is especially suitable for building medium to large complex applications.\n\nIt features a profiled assembly. On the one hand, we disassemble a large page into separate Component|Adapters. The upper layer is responsible for assembly, and the lower layer is responsible for implementation. On the other hand, the Component|Adapter is split into separate context-independent functions such as View, Reducer, and Effect.\n\nSo it will be very clean, easy to maintain, and easy to collaborate.\n\nFish Redux is inspired by the excellent frameworks such as Redux, Elm, Dva.\n\nStanding on the shoulders of giants, Fish Redux will do it further on the characteristics of concentration, division, reuse and isolation.\n"
  },
  {
    "path": "docs/.nojekyll",
    "content": ""
  },
  {
    "path": "docs/README.md",
    "content": "<p align=\"center\"><img src=\"https://img.alicdn.com/tfs/TB1r74NJyLaK1RjSZFxXXamPFXa-1024-1024.png\" align=\"center\" width=\"175\"></p>\n<h1 align=\"center\">Fish Redux</h1>\n\n[![Build Status](https://travis-ci.org/alibaba/fish-redux.svg?branch=master)](https://travis-ci.org/alibaba/fish-redux) [![pub package](https://img.shields.io/pub/v/fish_redux.svg)](https://pub.dartlang.org/packages/fish_redux) [![codecov](https://codecov.io/gh/alibaba/fish-redux/branch/master/graph/badge.svg)](https://codecov.io/gh/alibaba/fish-redux)\n\n## What is Fish Redux ?\n\nFish Redux is an assembled flutter application framework based on Redux state management.\nIt is suitable for building medium and large applications.\n\nIt has four characteristics:\n\n> 1. Functional Programming\n\n> 2. Predictable state container\n\n> 3. Pluggable componentization\n\n> 4. Non-destructive performance\n\n## Architecture diagram\n\n<img src=\"https://img.alicdn.com/tfs/TB1pkhoJr2pK1RjSZFsXXaNlXXa-1004-1370.png\" width=\"500px\" height=\"680px\">\n\n## Installation\n\n[Go](https://pub.dartlang.org/packages/fish_redux#-installing-tab-)\n\n## Documentation\n\nLanguage: [English](/) | [中文简体](/zh-cn/)\n\n## Examples\n\n-   [todo list](example) - a simple todo list demo.\n-   run it:\n\n```\ncd ./example\nflutter create .\nflutter run\n```\n\n## What's the difference between 'Fish Redux' and 'Redux' ?\n\n-   [answers](doc/concept/what's-the-diiference.md)\n\n## Plugins\n\n### Code Template\n\n-   [Fish Redux Template For Android Studio](https://github.com/BakerJQ/FishReduxTemplateForAS), by BakerJQ.\n-   [Fish Redux Template For VSCode](https://github.com/huangjianke/fish-redux-template), by huangjianke.\n\n### Dev-Tools\n\n-   Redux Inspector (using [Flutter Debugger](https://github.com/blankapp/flutter-debugger) and [flipperkit_fish_redux_middleware](https://pub.dartlang.org/packages/flipperkit_fish_redux_middleware)) for Fish Redux apps, by [JianyingLi](https://github.com/lijy91)\n\n## License\n\n-   Fish Redux is released under the Apache 2.0 license. See [LICENSE](LICENSE) for details.\n\n\n## 关于我们\n阿里巴巴-闲鱼技术是国内最早也是最大规模线上运行Flutter的团队。\n\n我们在公众号中为你精选了Flutter独家干货，全面而深入。\n\n内容包括：Flutter的接入、规模化应用、引擎探秘、工程体系、创新技术等教程和开源信息。\n\n**架构／服务端／客户端／前端／质量工程师 在公众号中投递简历，名额不限哦**\n\n欢迎来闲鱼做一个好奇、幸福、有影响力的程序员，简历投递：tino.wjf@alibaba-inc.com\n\n订阅地址\n\n<img src=\"https://img.alicdn.com/tfs/TB17Ki5XubviK0jSZFNXXaApXXa-656-656.png\" width=\"328px\" height=\"328px\">\n\n[For English](https://twitter.com/xianyutech \"For English\")\n"
  },
  {
    "path": "docs/_navbar.md",
    "content": "* [En](/)\n* [中文](/zh-cn/)\n"
  },
  {
    "path": "docs/_sidebar.md",
    "content": "* [Redux](concept/redux.md)\n* [Action](concept/action.md)\n* [Connector](concept/connector.md)\n* [Reducer](concept/reducer.md)\n* [Middleware](concept/middleware.md)\n* [Component](concept/component.md)\n* [View](concept/view.md)\n* [Reducer](concept/reducer.md)\n* [Effect](concept/effect.md)\n* [HigherEffect](concept/higher-effect.md)\n* [Lifecycle](concept/lifecycle.md)\n* [Dependencies](concept/dependencies.md)\n* [Dependent](concept/dependent.md)\n* [ShouldUpdate](concept/should-update.md)\n* [OnError](concept/on-error.md)\n* [Filter](concept/filter.md)\n* [OOP](concept/oop.md)\n* [WidgetWrapper](concept/widget-wrapper.md)\n* [Page](concept/page.md)\n* [Adapter](concept/adapter.md)\n* [StaticFlowAdapter](concept/static-flow-adapter.md)\n* [DynamicFlowAdapter](concept/dynamic-flow-adapter.md)\n* [CustomAdapter](concept/custom-adapter.md)\n* [What's the difference between 'Fish Redux' and 'Redux' ?](concept/what's-the-diiference.md)\n* [What's-adapter](concept/what's-adapter.md)\n* [What's-connector](concept/what's-connector.md)\n* [Mechanism](concept/mechanism.md)\n* [Directory](concept/directory.md)\n"
  },
  {
    "path": "docs/concept/_navbar.md",
    "content": "* [En](/)\n* [中文](/zh-cn/)\n"
  },
  {
    "path": "docs/concept/_sidebar.md",
    "content": "* [Redux](concept/redux.md)\n* [Action](concept/action.md)\n* [Connector](concept/connector.md)\n* [Reducer](concept/reducer.md)\n* [Middleware](concept/middleware.md)\n* [Component](concept/component.md)\n* [View](concept/view.md)\n* [Reducer](concept/reducer.md)\n* [Effect](concept/effect.md)\n* [HigherEffect](concept/higher-effect.md)\n* [Lifecycle](concept/lifecycle.md)\n* [Dependencies](concept/dependencies.md)\n* [Dependent](concept/dependent.md)\n* [ShouldUpdate](concept/should-update.md)\n* [OnError](concept/on-error.md)\n* [Filter](concept/filter.md)\n* [OOP](concept/oop.md)\n* [WidgetWrapper](concept/widget-wrapper.md)\n* [Page](concept/page.md)\n* [Adapter](concept/adapter.md)\n* [StaticFlowAdapter](concept/static-flow-adapter.md)\n* [DynamicFlowAdapter](concept/dynamic-flow-adapter.md)\n* [CustomAdapter](concept/custom-adapter.md)\n* [What's the difference between 'Fish Redux' and 'Redux' ?](concept/what's-the-diiference.md)\n* [What's-adapter](concept/what's-adapter.md)\n* [What's-connector](concept/what's-connector.md)\n* [Mechanism](concept/mechanism.md)\n* [Directory](concept/directory.md)\n"
  },
  {
    "path": "docs/concept/action.md",
    "content": "# Action\n\n-   Action contains two fields\n    -   type\n    -   payload\n-   Recommended way of writing action\n    -   Create an action.dart file for a component|adapter that contains two classes\n        -   An enumeration class for the type field\n        -   An ActionCreator class is created for the creator of the Action, which helps to constrain the type of payload.\n    -   Effect Accepted Action which's type is named after `on{verb}`\n    -   Reducer Accepted Action which's type is named after `{verb}`\n    -   Sample code\n\n```dart\nenum MessageAction {\n    onShare,\n    shared,\n}\n\nclass MessageActionCreator {\n    static Action onShare(Map<String, Object> payload) {\n        return Action(MessageAction.onShare, payload: payload);\n    }\n\n    static Action shared() {\n        return const Action(MessageAction.shared);\n    }\n}\n```\n"
  },
  {
    "path": "docs/concept/adapter.md",
    "content": "# Adapter\n\n-   In addition to the concept of the underlying Component, we have added a componentized abstract Adapter. Its goal is to solve the 3 problems of the Component model in the ListView scene.\n\n    -   1）Putting a \"Big-Cell\" in the ListView does not enjoy the performance optimization of the ListView code.\n    -   2）Component cannot distinguish between the appear|disappear and init|dispose events.\n    -   3）The life cycle of the Effect and the coupling of the View do not meet the intuitive expectations in some scenes of the ListView.\n\n-   An Adapter and a Component are almost identical except for the following points\n\n    -   Component generates a Widget, Adapter generates a ListAdapter, and ListAdapter has the ability to generate a list of Widgets.。\n        -   Not specifically generating a Widget but a ListAdapter can greatly improve the page frame rate and fluency.\n    -   Effect-Lifecycle-Promote\n        -   The Effect of Component follows the life cycle of the Widget, and the Adapter's Effect follows the life cycle of the parent Widget.\n        -   The improvement of the life cycle of the effect greatly removes the coupling between the business logic and the view life. Even if its display has not yet appeared, other modules can still call its capabilities through dispatch-api.\n    -   Appearance|disappear event notification\n        -   As the Effect lifecycle improves, we can more closely distinguish between init|dispose and appear|disappear. This is indistinguishable from the Model's model.\n    -   Reducer is long-lived, Effect is medium-lived, View is short-lived.\n\n-   Three implementations of Adapter\n    -   [DynamicFlowAdapter](dynamic-flow-adapter.md)\n    -   [StaticFlowAdapter](static-flow-adapter.md)\n    -   [CustomAdapter](custom-adapter.md)\n"
  },
  {
    "path": "docs/concept/auto-dispose.md",
    "content": "# Auto-Dispose\n\n-   AutoDispose is a very simple way to manage lifecycle objects. An auto-dispose object can be released on its own initiative or released when the managed object it follows is released.\n-   The Context used in Effect and the EffectPart in HigherEffect are auto-dispose objects. So we can easily host custom objects that need to be managed for lifecycle management.\n-   Sample Code\n\n```dart\nclass ItemWidgetBindingObserver extends WidgetsBindingObserver\n    with AutoDispose {\n  ItemWidgetBindingObserver() : super() {\n    WidgetsBinding.instance.addObserver(this);\n  }\n\n  @override\n  void didChangeAppLifecycleState(AppLifecycleState state) {\n    if (AppConfig.flutterBinding.framesEnabled &&\n        state == AppLifecycleState.resumed) {\n      AppConfig.flutterBinding.performReassemble();\n    }\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n    WidgetsBinding.instance.removeObserver(this);\n  }\n}\n\nvoid _init(Action action, Context<ItemPageContainerState> ctx) {\n    final ItemWidgetBindingObserver observer = ItemWidgetBindingObserver();\n    observer.follow(ctx);\n}\n\n```\n"
  },
  {
    "path": "docs/concept/component.md",
    "content": "# Component\n\nComponent is the encapsulation of view presentation and logic functions.\nFor the moment, from the perspective of Redux, we divide the component into state-manage functions (Reducers) and others.\nLooking to the future, from the perspective of UI-Automation, we divide the component into presentations and others.\n\nCombining the above two perspectives, we got the three parts of View, SideEffect, and Reducer, which are called the three factors of the component.\n\nWe use explicit configuration to complete the registration of components and adapters on which large component depend. This dependency configuration is called Dependencies.\n\nSo with this formula:\nComponent = View + Effect(Optional) + Reducer(Optional) + Dependencies(Optional)\n\nDivision: From the perspective of the component\n<img src=\"https://img.alicdn.com/tfs/TB1vqB2J4YaK1RjSZFnXXa80pXa-900-780.png\" width=\"450px\" height=\"390px\">\n\nConcentration: From the perspective of the Store\n<img src=\"https://img.alicdn.com/tfs/TB1sThMJYvpK1RjSZFqXXcXUVXa-1426-762.png\" width=\"713px\" height=\"381px\">\n"
  },
  {
    "path": "docs/concept/connector.md",
    "content": "# Connector<T, P>\n\n-   It expresses a data connection relationship of how to read small data from a big data, and how to synchronize to big data when the small data is modified。\n-   It is the key to a centralized Reducer that can be assembled automatically by a multi-level, multi-module, small Reducer\n    -   It greatly reduces the difficulty of using Redux. We no longer care about the assembly process, we care about what specific actions cause the state to change.\n-   It is used in the configuration Dependencies, in the configuration we have solidified the connection between the large component and the small component, so we do not need to pass in any dynamic parameters when we use the small component.\n-   ![image.png | left | 719x375](https://cdn.nlark.com/lark/0/2018/png/82574/1545365202743-01074be7-f067-45c7-aae0-91b12cd50ae6.png)\n\n-   Sample Code\n\n```dart\nclass DetialState {\n    Profile profile;\n    String message;\n}\n\nConnOp<DetialState, String> messageConnector() {\n    return ConnOp<DetialState, String>(\n        get: (DetialState state) => state.message,\n        set: (DetialState state, String message) => state.message = message,\n    );\n}\n```\n"
  },
  {
    "path": "docs/concept/custom-adapter.md",
    "content": "# CustomAdapter\n\n-   Custom implementation of large Cell in LisView.\n-   The Factors of the Adapter are similar to the Component's. The difference is that the view part of the Adapter returns a ListAdapter.\n-   Sample Code\n\n```dart\nclass CommentAdapter extends Adapter<CommentState> {\n    CommentAdapter()\n        : super(\n            adapter: buildCommentAdapter,\n            effect: buildCommentEffect(),\n            reducer: buildCommentReducer(),\n        );\n}\n\nListAdapter buildCommentAdapter(CommentState state, Dispatch dispatch, ViewService service) {\n    final List<IndexedWidgetBuilder> builders = Collections.compact(<IndexedWidgetBuilder>[]\n    ..add((BuildContext buildContext, int index) =>\n        _buildDetailCommentHeader(state, dispatch, service))\n    ..addAll(_buildCommentViewList(state, dispatch, service))\n    ..add(isEmpty(state.commentListRes?.items)\n        ? (BuildContext buildContext, int index) =>\n            _buildDetailCommentEmpty(state.itemInfo, dispatch)\n        : null)\n    ..add(state.commentListRes?.getHasMore() == true\n        ? (BuildContext buildContext, int index) => _buildLoadMore(dispatch)\n        : null));\n    return ListAdapter(\n    (BuildContext buildContext, int index) =>\n        builders[index](buildContext, index),\n    builders.length,\n    );\n}\n\n///builds\n```\n"
  },
  {
    "path": "docs/concept/dependencies.md",
    "content": "# Dependencies\n\n-   Dependencies is a structure that expresses dependencies between components. It accepts two fields\n    -   slots\n        -   <String, [Dependent](dependent.md)>{}\n    -   [adapter](adapter.md)\n-   It mainly contains three aspects of information\n    -   The slots that the component depends on.\n    -   The adapter that the component depends on (used to build a high-performance ListView).\n    -   [Dependent](dependent.md) Is a combination of subComponent | subAdapter + [connector](connector.md)。\n    -   A component's [Reducer](reducer.md) is automatically compounded by the Reducer configured by the Component itself and all of the Reducers under its Dependencies.\n-   Sample Code\n\n```dart\n///register in component\nclass ItemComponent extends ItemComponent<ItemState> {\n  ItemComponent()\n      : super(\n          view: buildItemView,\n          reducer: buildItemReducer(),\n          dependencies: Dependencies<ItemState>(\n            slots: <String, Dependent<ItemState>>{\n              'appBar': AppBarComponent().asDependent(AppBarConnector()),\n              'body': ItemBodyComponent().asDependent(ItemBodyConnector()),\n              'ad_ball': ADBallComponent().asDependent(ADBallConnector()),\n              'bottomBar': BottomBarComponent().asDependent(BottomBarConnector()),\n            },\n          ),\n        );\n}\n\n///call in view\nWidget buildItemView(ItemState state, Dispatch dispatch, ViewService service) {\n  return Scaffold(\n      body: Stack(\n        children: <Widget>[\n          service.buildComponent('body'),\n          service.buildComponent('ad_ball'),\n          Positioned(\n            child: service.buildComponent('bottomBar'),\n            left: 0.0,\n            bottom: 0.0,\n            right: 0.0,\n            height: 100.0,\n          ),\n        ],\n      ),\n      appBar: AppbarPreferSize(child: service.buildComponent('appBar')));\n}\n```\n"
  },
  {
    "path": "docs/concept/dependent.md",
    "content": "### Dependent\n\n-   Dependent = connector<T, P> + subComponent | subAdapter\n-   It expresses how the small component or adapter are connected to it's parent component.\n-   Sample Code\n\n```dart\n/// todo\n```\n"
  },
  {
    "path": "docs/concept/directory.md",
    "content": "# Directory\n\nThe recommended directory structure\n\n```\nsample_page\n    -- action.dart /// define action types and action creator\n    -- page.dart /// config a page or component\n    -- view.dart /// define a function which expresses the presentation of user interface\n    -- effect.dart /// define a function which handles the side-effect\n    -- reducer.dart /// define a function which handles state-change\n    -- state.dart /// define a state and some connector of substate\n    components\n        sample_component\n        -- action.dart\n        -- component.dart\n        -- view.dart\n        -- effect.dart\n        -- reducer.dart\n        -- state.dart\n```\n\nThe upper layer is responsible for assembly and the lower layer is responsible for implementation.\n"
  },
  {
    "path": "docs/concept/dynamic-flow-adapter.md",
    "content": "# DynamicFlowAdapter\n\n-   The template is a Map that accepts an array-like data driven\n-   Sample Code\n\n```dart\nclass RecommendAdapter extends DynamicFlowAdapter<RecommendState> {\n    RecommendAdapter()\n        : super(\n            pool: <String, Component<Object>>{\n                'card_0': RecommendTitleComponent(),\n                'card_1': RecommendRowComponent(),\n            },\n            connector: RecommendCardListConnector(),\n        );\n}\n```\n\n<img src=\"https://img.alicdn.com/tfs/TB10lxHLMDqK1RjSZSyXXaxEVXa-1838-1024.png\" width=\"919px\" height=\"512px\">\n"
  },
  {
    "path": "docs/concept/effect.md",
    "content": "# Effect\n\n-   Effect is a function that handles all side effects. It receives the following parameters\n    -   Action action\n    -   Context context\n        -   BuildContext context\n        -   T state\n        -   dispatch\n        -   isDisposed\n-   It mainly contains four aspects of information\n    -   Receive \"intent\" from the View, including the corresponding lifecycle callback, and then make specific execution.\n    -   Its processing may be an asynchronous function, the data may be changed in the process, so we should get the latest data through context.state.\n    -   If you want to modify the data, you should send an Action to the Reducer to handle. It is read-only for data and cannot be modified directly in a effect function.\n    -   If its return value is a non-null value, it will take precedence for itself and will not do the next step; otherwise it will broadcast to the Effect part of other components and sent the action to the Reducer.\n\n> Self-First-Broadcast。\n> ![image.png | left | 747x399](https://cdn.nlark.com/lark/0/2018/png/82574/1545365233153-4c8105b4-050c-49e6-be02-dbf28a861caa.png)\n\n-   Sample Code\n\n```dart\n/// one style of writing\nFutureOr<Object> sideEffect(Action action, Context<String> ctx) async {\n  if (action.type == Lifecycle.initState) {\n    //do something on initState\n    return true;\n  } else if (action.type == 'onShare') {\n    //do something on onShare\n    await Future<void>.delayed(Duration(milliseconds: 1000));\n    ctx.dispatch(const Action('shared'));\n    return true;\n  }\n  return null;\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: sideEffect,\n        );\n}\n```\n\n```dart\n/// another style of writing\nEffect<String> buildEffect() {\n  return combineEffects(<Object, Effect<String>>{\n    Lifecycle.initState: _initState,\n    'onShare': _onShare,\n  });\n}\n\nvoid _initState(Action action, Context<String> ctx) {\n  //do something on initState\n}\n\nvoid _onShare(Action action, Context<String> ctx) async {\n  //do something on onShare\n  await Future<void>.delayed(Duration(milliseconds: 1000));\n  ctx.dispatch(const Action('shared'));\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n        );\n}\n```\n"
  },
  {
    "path": "docs/concept/evolution-of-fish-redux.md",
    "content": "# fish-redux 的演进史\n\nfish-redux 是一个不断演进的框架，甚至是在不断的回炉重造，在这个过程中\n\n<img src=\"https://img.alicdn.com/tfs/TB1aeJELpzqK1RjSZFCXXbbxVXa-1794-938.png\" width=\"897px\" height=\"469px\">\n\n-   1. 第一个版本是基于社区内的 flutter_redux 进行的改造，核心是提供了 UI 代码的组件化，当然问题也非常明显，针对复杂的业务场景，往往业务逻辑很多，无法做到逻辑代码的分治和复用。\n\n-   2. 第二个版本针对第一个版本的问题，做出了比较重大的修改，解决了 UI 代码和逻辑代码的分治问题，但设计上打破了 redux 的原则，丢失了 Redux 的精华。\n\n-   3. 在第三个版本进行重构时，我们确立了整体的架构原则与分层要求，一方面按照 reduxjs 的代码进行了 flutter 侧的 redux 实现，将 redux 完整保留下来。另一方面针对组件化的问题，提供了 redux 之上的 component 的封装，并创新的通过这一层的架构设计提供了业务代码分治的能力。第三版 完成了 Redux， Component 两层的设计，其中包含了 Connector，Dependencies，Context 等重要概念。\n\n    -   3.1 解决集中和分治的矛盾的核心在于 [Connector](what's-connector.md)\n    -   3.2 这一层的组件的分治是面向通用设计的。通过在 [Dependencies](dependencies-cn.md) 配置 slots，得到了可插拔的组件系统。\n\n-   4. 在第三个版本 Redux & Component 之外，提供了面向 ListView 场景的分治设计 Adapter。\n    -   解决了在面向 ListView 场景下的逻辑的分治和性能降低的矛盾。\n    -   [what's-adapter](what's-adapter.md)\n\n> 目前，fish redux 已经在闲鱼线上稳定运行，未来，期待 fish redux 给社区带来更多的输入。\n"
  },
  {
    "path": "docs/concept/features.md",
    "content": "# Features\n\n## 直接使用 flutter 会面临的问题？\n\n> [flutter](https://github.com/flutter/flutter) 是 google 推出的新一代跨平台渲染框架.\n> 它帮助开发者解决了跨平台，高性能，富有表现力和灵活的 UI 表达，快速开发等核心问题。\n> 但是如果开发大应用，还需要解决以下问题。\n>\n> > 1. 数据流问题\n> > 2. 通信问题\n> > 3. 可插拔的组件系统\n> > 4. 展示和逻辑解耦\n> > 5. 统一的编程模型和规范\n>\n> 我们可以类比 flutter 和 React，事实上在中大型应用中 React 会面临的绝大多数问题，flutter 也同样面临考验。\n\n## 数据流问题\n\n> 目前社区流行的数据流方案有：\n> 单向数据流方案，以 Redux 为代表\n> 响应式数据流方案，以 Mobx 为代表\n> 其他，以 rxjs 为代表\n> 那么哪一种架构最合适 flutter ？\n> 我们追随了 javascript 栈绝大多数开发者的选择 - [ReduxJs](https://github.com/reduxjs/redux)\n> 感谢 ReduxJs，我们是几乎 100%的还原了它在 dart 上的实现。所以我们也继承了它的优点：[Predictable],[Centralized],[Debuggable],[Flexible]。\n\n## 通信问题\n\n> 直接使用 flutter，在 Widgets 之间传递状态和回调，随着应用复杂度的上升，会变成是一件可怕而糟糕的事情。\n> 通过 fish redux，依托于集中的 Redux 和分治的 Effect 模块，通过一个极简的 [dispatch-api](mechanism.md)，完成所有的通信的诉求。\n\n## 可插拔的组件系统\n\n> fish redux 通过一个配置式的 Dependencies，来完成灵活的可插拔的组件系统。同时有这一配置的存在，它解放了我们手动拼装 Reducer 的繁琐工作。\n> 参考:\n>\n> > 1. [what's-connector](what's-connector.md)\n> > 2. [connector](connector.md)\n> > 3. [dependencies](dependencies.md)\n> > 4. [component](component.md)\n> > 5. [adapter](adapter.md)\n> > 6. [what's-adapter](what's-adapter.md)\n\n## 展示和逻辑解耦\n\n> fish redux 从 [elm](https://guide.elm-lang.org/) 中得到了非常多的设计灵感。\n> 将一个组件，拆分为相互独立的 View，Effect，Reducer 三个函数，我们优雅的解决了展示和逻辑解耦的问题。\n> 通过这样的拆分，我们将 UI 的表达隔离于一个函数内，它让我们更好的面向未来，一份 UI 表达它可能来自于开发者，可能来自于深度学习框架的 UI 代码生成，可能是面向移动终端，也可能是面向浏览器。它让我们有了更多的组合的可能。\n> 同时函数式的编程模型带来了更容易编写，更容易扩展，更容易测试，更容易维护等特性。\n\n## 统一的编程模型和规范\n\n> [directory](directory.md)\n"
  },
  {
    "path": "docs/concept/filter.md",
    "content": "# Filter\n\n-   Filter is used to optimize the performance of the Reducer. Because the Reducer is layer-assembled, each Action is processed, and in theory, all the small Reducers are traversed. In some very complicated scenarios, such a deep traversal may take up to the millisecond level (generally Should be less than 1 millisecond). Then we need to optimize the performance of the Reducer, decide in advance whether to traverse this Reducer subtree, reduce the depth and number of traversal.\n-   Sample Code\n\n```dart\nbool filter(Action action) {\n    return action.type == 'some action';\n}\n```\n"
  },
  {
    "path": "docs/concept/higher-effect.md",
    "content": "# HigherEffect\n\n-   Since Effect may have some temporary state of its own (although it is not recommended, support is provided), in order to support this feature, we promote the Effect to a higher-order function and put its state in the closure.\n-   The framework supports the configuration of Effect|HigherEffect on component or adapter, but it can't be configured for one component or adapter at the same time, which will cause trouble. In general, the configuration is often an explicit negligence.\n-   HigherEffect = (Context ctx) => (Action action) => FutureOr\n-   For more detailed examples, please refer to [OOP](oop.md) - EffectPart\n"
  },
  {
    "path": "docs/concept/lifecycle.md",
    "content": "# Lifecycle\n\n-   The default all lifecycles are essentially derived from the lifecycle in flutter State<StatefulWidget>.\n    -   initState\n    -   didChangeDependencies\n    -   build\n    -   didUpdateWidget\n    -   deactivate\n    -   dispose\n-   Within the component, the Lifecycle of the Reducer is consistent with the page, and the lifecycle of Effect and View is consistent with the component's Widget.\n-   In the adapter, the Lifecycle of the Reducer is consistent with the page. The life cycle of the Effect is the same as the life cycle of the ListView. The life cycle of the View is short-lived (destroyed in the invisible area). At the same time, the life cycle of appear and disappear is added, representing the view array managed by this adapter, the callback just entering the display area and completely leaving the display area.\n"
  },
  {
    "path": "docs/concept/mechanism.md",
    "content": "# Communication Mechanism\n\n## Page internal communication\n\n-   Component internal communication\n-   Inter-component communication\n\n![image.png | left | 747x399](https://cdn.nlark.com/lark/0/2018/png/82574/1545365233153-4c8105b4-050c-49e6-be02-dbf28a861caa.png)\n\nSelf-First-Broadcast。\nThe emitted Action will be processed first by its own Effect, otherwise it will be broadcast to other components and Redux.\n\nWe completed the communication between the components (parent to child, child to parent, brother, etc.) through a simple and intuitive dispatch.\n\n## Inter-page communication\n\n-   Context.appBroadcast\n    -   Each page's PageStore receives an action which is handled independently.\n\n![image.png | left | 691x519](https://cdn.nlark.com/lark/0/2018/png/82574/1545368705599-745c46a3-f5c6-41a7-a757-1bc6f9a389d4.png)\n\n# Refresh Mechanism\n\n## 数据刷新\n\n-   Local data modification automatically triggers a shallow copy of the upper layer data and is transparent to the business code.\n\n![image.png | left | 747x361](https://cdn.nlark.com/lark/0/2018/png/82574/1545386668521-0081cb5f-8017-47d1-ad7c-8802bb0be8a0.png)\n\n## View refresh\n\n-   When the state changes, the store flatly notifies all the components and the [ShouldUpdate](should-update.md) decide whether the view should be refreshed\n\n![image.png | left | 747x336](https://cdn.nlark.com/lark/0/2018/png/82574/1545386773247-2eddfa99-e6b9-4be9-ac43-d1944ff44e9b.png)\n"
  },
  {
    "path": "docs/concept/middleware-cn.md",
    "content": "# Middleware\n\n关于 Middleware 的定义、签名和 ReduxJS 社区是一致的。\n\n示例代码\n\n```dart\nMiddleware<T> logMiddleware<T>({\n  String tag = 'redux',\n  String Function(T) monitor,\n}) {\n  return ({Dispatch dispatch, Get<T> getState}) {\n    return (Dispatch next) {\n      return isDebug()\n          ? (Action action) {\n              print('---------- [$tag] ----------');\n              print('[$tag] ${action.type} ${action.payload}');\n\n              final T prevState = getState();\n              if (monitor != null) {\n                print('[$tag] prev-state: ${monitor(prevState)}');\n              }\n\n              next(action);\n\n              final T nextState = getState();\n              if (monitor != null) {\n                print('[$tag] next-state: ${monitor(nextState)}');\n              }\n\n              if (prevState == nextState) {\n                print('[$tag] warning: ${action.type} has not been used.');\n              }\n\n              print('========== [$tag] ================');\n            }\n          : next;\n    };\n  };\n}\n```\n\n更多的参考 src/utils/common_middleware\n"
  },
  {
    "path": "docs/concept/middleware.md",
    "content": "# Middleware\n\n-   The definition and signature of Middleware is consistent with the ReduxJS community.\n-   Sample Code\n\n```dart\nMiddleware<T> logMiddleware<T>({\n  String tag = 'redux',\n  String Function(T) monitor,\n}) {\n  return ({Dispatch dispatch, Get<T> getState}) {\n    return (Dispatch next) {\n      return isDebug()\n          ? (Action action) {\n              print('---------- [$tag] ----------');\n              print('[$tag] ${action.type} ${action.payload}');\n\n              final T prevState = getState();\n              if (monitor != null) {\n                print('[$tag] prev-state: ${monitor(prevState)}');\n              }\n\n              next(action);\n\n              final T nextState = getState();\n              if (monitor != null) {\n                print('[$tag] next-state: ${monitor(nextState)}');\n              }\n\n              if (prevState == nextState) {\n                print('[$tag] warning: ${action.type} has not been used.');\n              }\n\n              print('========== [$tag] ================');\n            }\n          : next;\n    };\n  };\n}\n```\n\n更多的参考 src/utils/common_middleware\n"
  },
  {
    "path": "docs/concept/on-error.md",
    "content": "# OnError\n\n-   Centralizes the business exceptions generated by Effect, whether it is a synchronous function or an asynchronous function. With a unified exception handling mechanism, we can stand on a higher level of abstraction and make reasonable simplifications of business code.\n-   Sample Code\n\n```dart\nbool onMessageError(Exception e, Context<String> ctx) {\n    if(e is BizException) {\n        ///do some toast\n        return true;\n    }\n    return false;\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n            reducer: buildMessageReducer(),\n            onError: onMessageError,\n        );\n}\n```\n"
  },
  {
    "path": "docs/concept/oop.md",
    "content": "# OOP\n\n-   Although the framework recommends the use of functional programming, it also provides object-oriented programming support.\n    -   ViewPart\n        -   Need to override the 'build' function.\n        -   The required state, dispatch, and viewService parameters have become fields of the object and can be used directly.\n        -   It is immutable, so there should be no need to define variable fields internally.\n    -   EffectPart\n        -   Need to override the 'createMap' function.\n        -   The required Context has been flattened as the fields which can be used directly.\n        -   Fields can be defined and their visibility is limited to themselves.\n        -   It must be used with higherEffect.\n-   Sample Code\n\n```dart\nclass MessageView extends ViewPart<MessageState> {\n    @override\n    Widget build() {\n        return Column(children: [\n            viewService.buildComponent('profile'),\n            InkWell(\n                child: Text('$message'),\n                onTap: () => dispatch(const Action('onShare')),\n            ),\n        ]);\n    }\n}\n\nclass MessageEffect extends EffectPart<MessageState> {\n    ///we could put some Non-UI fields here.\n\n    @override\n    Map<Object, OnAction> createMap() {\n        return <Object, OnAction>{\n            Lifecycle.initState: _initState,\n            'onShare': _onShare,\n        };\n    }\n\n    void _initState(Action action) {\n        //do something on initState\n    }\n\n    void _onShare(Action action) async {\n        //do something on onShare\n        await Future<void>.delayed(Duration(milliseconds: 1000));\n        dispatch(const Action('shared'));\n    }\n}\n\nclass MessageComponent extends Component<MessageState> {\n    MessageComponent(): super(\n        view: MessageView().asView(),\n        higherEffect: higherEffect(() => MessageEffect()),\n    );\n}\n```\n"
  },
  {
    "path": "docs/concept/page.md",
    "content": "# Page\n\n-   One and only one store in one page\n-   Page inherits from Component, so it can configure all the factors of Component.\n-   Page can configure Middleware for AOP management of Redux.\n-   Page must be configured with an initialization function that initializes page data initState.\n    <img src=\"https://img.alicdn.com/tfs/TB1ASfDJ9zqK1RjSZFHXXb3CpXa-1636-756.png\" width=\"818px\" height=\"378px\">\n\n-   Sample Code\n\n```dart\n/// Hello World\nclass HelloWordPage extends Page<String, String> {\n    HelloWordPage():\n        super(\n            initState: (String msg) => msg,\n            view:(String msg, _, __) => Text('Hello ${msg}'),\n        );\n}\n\nHelloWordPage().buildPage('world')\n```\n"
  },
  {
    "path": "docs/concept/reducer.md",
    "content": "# Reducer\n\n-   The Reducer is a context-independent pure function. It receives the following parameters\n    -   T state\n    -   Action action\n-   It mainly contains three aspects of information\n    -   Receive an \"intent\" and make a state modification.\n    -   If you want to modify the state, you need to create a new copy and modify it on the copy.\n    -   If the small state is modified, it will automatically trigger the copy of the main state's layers data, and then notify the components to refresh in a flattened manner.\n-   Sample Code\n\n```dart\n/// one style of writing\nString messageReducer(String msg, Action action) {\n  if (action.type == 'shared') {\n    return '$msg [shared]';\n  }\n  return msg;\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n            reducer: messageReducer,\n        );\n}\n```\n\n```dart\n/// another style of writing\nReducer<String> buildMessageReducer() {\n  return asReducer(<Object, Reducer<String>>{\n    'shared': _shared,\n  });\n}\n\nString _shared(String msg, Action action) {\n  return '$msg [shared]';\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n            reducer: buildMessageReducer(),\n        );\n}\n```\n\n> 推荐的是第二种写法\n"
  },
  {
    "path": "docs/concept/redux.md",
    "content": "### Redux\n\n-   State\n-   [Action](action.md)\n-   [Reducer](reducer.md)\n-   Store\n-   [Middleware](middleware.md)\n    The above concepts are exactly the same as the community's Redux.\n    Redux is a framework for state management with [predictable][centralized] [easy to debug][flexibility].\n    If you want to have a closer understanding of Redux, please refer to [https://github.com/reduxjs/redux](https://github.com/reduxjs/redux)\n"
  },
  {
    "path": "docs/concept/should-update.md",
    "content": "# ShouldUpdate\n\n-   When the state changes, the store flatly notifies all the components.\n-   By default, the framework uses identical to compare the old and new state to determine if a refresh is needed.\n-   If we have a very precise request for component refresh, then we can define a ShouldUpdate ourselves.\n-   Sample Code\n\n```dart\nbool shouldUpdate(DetailState old, DetailState now) {\n    return old.message != now.message;\n}\n```\n"
  },
  {
    "path": "docs/concept/static-flow-adapter.md",
    "content": "# StaticFlowAdapter\n\n-   The template is an Array that accepts map-like data driven.\n-   The template receives an array of Dependents.\n-   It's similar with a flatMap + compact operation abstractly.\n-   Sample Code\n\n```dart\nclass ItemBodyComponent extends Component<ItemBodyState> {\n    ItemBodyComponent()\n        : super(\n            view: buildItemBody,\n            dependencies: Dependencies<ItemBodyState>(\n            adapter: StaticFlowAdapter<ItemBodyState>(\n                slots: <Dependent<ItemBodyState>>[\n                    VideoAdapter().asDependent(videoConnector()),\n                    UserInfoComponent().asDependent(userInfoConnector()),\n                    DescComponent().asDependent(descConnector()),\n                    ItemImageComponent().asDependent(itemImageConnector()),\n                    OriginDescComponent().asDependent(originDescConnector()),\n                    VisitComponent().asDependent(visitConnector()),\n                    SameMoreComponent().asDependent(sameMoreConnector()),\n                    PondComponent().asDependent(pondConnector()),\n                    CommentAdapter().asDependent(commentConnector()),\n                    RecommendAdapter().asDependent(recommendConnector()),\n                    PaddingComponent().asDependent(paddingConnector()),\n                ]),\n            ),\n        );\n}\n\n```\n\n<img src=\"https://img.alicdn.com/tfs/TB1sXXOLQvoK1RjSZPfXXXPKFXa-1666-1104.png\" width=\"833px\" height=\"552px\">\n"
  },
  {
    "path": "docs/concept/view.md",
    "content": "# View\n\n-   View is a context-independent function that outputs Widget. It receives the following parameters\n    -   T state\n    -   Dispatch\n    -   ViewService\n-   It mainly contains three aspects of information\n    -   The view is completely driven by data.\n    -   The event/callback triggered by the view, use Dispatch to send \"intent\", but never do a specific implementation.\n    -   Use dependent component/adapter, by explicitly configuring it on the parent component, and then standardizing calls through the ViewService.\n        -   Where ViewService provides three capabilities\n            -   BuildContext context: Ability to get widget's BuildContext\n            -   Widget buildView(String name): Ability to create subcomponents directly\n                -   The name passed in here is the name configured on Dependencies.\n                -   Creating subcomponents does not require passing in any other parameters, since the parameters required by the subcomponents have been passed through the Dependencies configuration, and their data relationships are established via the connector.\n            -   ListAdapter buildAdapter(): Ability to create adapter directly\n-   Sample Code\n\n```dart\nWidget buildMessageView(String message, Dispatch dispatch, ViewService viewService) {\n  return Column(children: [\n    viewService.buildComponent('profile'),\n    InkWell(\n      child: Text('$message'),\n      onTap: () => dispatch(const Action('onShare')),\n    ),\n  ]);\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n        );\n}\n```\n"
  },
  {
    "path": "docs/concept/what's-adapter.md",
    "content": "# What's adapter\n\n面向 ListView 场景的分治设计 Adapter。\n\n> 在解答什么是 adapter 之前，我们来看下一般框架对 ListView 的分治是怎么做的。传统的手段，我们对 ListView 的分治更多的局限于它展现部分，而它的逻辑部分往往是集中的。而当我们试图将 ListView 下的某一局部的展现和逻辑封装在一起，我们就会遇到\"Big-Cell\"问题，面临性能的显著降低。\n> 这里面存在一个分治和性能上的矛盾。这个矛盾带来了复用难，可维护差，难以协作等中大型场景下的问题。\n>\n> 解决这个问题，有两种思路：\n>\n> 1. 下沉到 UI 表达层（Widgets），去实现一个高性能的 ScrollView。\n> 2. 向上做模型抽象，得到一个逻辑上的 ScrollView，性能上的 ListView。\n>\n> fish redux 选择了第二条更加通用的路径来解决 LisView 下的分治问题。\n>\n> 一个 ListView 对应了一个 Adapter，这看上去非常的像 Android 里的设计，但事实上 fish-redux 里的 Adapter 概念走的更远。\n>\n> 1. 一个 Adapter 是可以由多个 Component 和 Adapter 组合而成，它有点像 flatmap & compact 的 api 的叠加。\n> 2. Adapter 以及它的子 Adapter 的生命周期是和 ListView 是等效的。它像跨斗一般附着于 ListView 的生命周期之上。同时由于 Adapter 生命周期的提升，我们额外收获了两个非常有用的事件消息(appear & disappear)。\n>\n> > 注意 ⚠️ 在 Adapter 里配置的子 Component，它的生命周期和它所对应的 WidgetState 是一致的，所以它的是短暂的。\n\n-   Adapter 的容器有两类，用图来说明吧：\n\n<img src=\"https://img.alicdn.com/tfs/TB1sXXOLQvoK1RjSZPfXXXPKFXa-1666-1104.png\" width=\"833px\" height=\"552px\">\n\n<img src=\"https://img.alicdn.com/tfs/TB10lxHLMDqK1RjSZSyXXaxEVXa-1838-1024.png\" width=\"919px\" height=\"512px\">\n"
  },
  {
    "path": "docs/concept/what's-connector.md",
    "content": "# What's connector\n\n在解答 connector 是什么之前，我们来先看一个代码片段\n\n```javascript\nlet hasChanged = false;\nconst nextState = {};\nfor (let i = 0; i < finalReducerKeys.length; i++) {\n\tconst key = finalReducerKeys[i];\n\tconst reducer = finalReducers[key];\n\tconst previousStateForKey = state[key];\n\tconst nextStateForKey = reducer(previousStateForKey, action);\n\tif (typeof nextStateForKey === 'undefined') {\n\t\tconst errorMessage = getUndefinedStateErrorMessage(key, action);\n\t\tthrow new Error(errorMessage);\n\t}\n\tnextState[key] = nextStateForKey;\n\thasChanged = hasChanged || nextStateForKey !== previousStateForKey;\n}\nreturn hasChanged ? nextState : state;\n```\n\n以上来自于 Reduxjs-[combineReducers](https://github.com/reduxjs/redux/blob/master/src/combineReducers.js)的核心实现。\n\ncombineReducers 是一个将 Reducer 分治的函数，让一个庞大数据的 Reducer 可以由多层的更小的 Reducer 组合而成。\n\n这是 Redux 框架里的核心 API，但是他有缺点。他有非常明显的语言的局限性，如下 3 点:\n\n1. 浅拷贝一个任意对象\n\n```javascript\nconst nextState = {};\n```\n\n2. 读取字段\n\n```javascript\nconst previousStateForKey = state[key];\n```\n\n3. 写入字段\n\n```javascript\nnextState[key] = nextStateForKey;\n```\n\n将上面的 3 点抽象来看：\n\n1. State 的 clone 的能力（浅拷贝）\n2. Get & Set 的能力，即为 Connector 的概念。\n\n有了 以上两点，我们才完全集成了 Redux 的所有精华，同时将它的设计更上一个通用的维度。\n"
  },
  {
    "path": "docs/concept/what's-the-diiference.md",
    "content": "# What's different with Redux ?\n\n## They are two frameworks for solving problems at different layers.\n\n> Redux is a framework focused on state management; Fish Redux is an application framework based on Redux for state management.\n\n> The application framework not only solves the problem of state management, but also solves the problems of divide and conquer, communication, data drive, decoupling and so on.\n\n## Fish Redux solves the contradiction between concentration and division.\n\n> Redux completes the merge process from the small Reducers to the main Reducer by the user manually organizing the code;\n\n> Fish Redux automatically completes the merge process from the small Reducers to the main Reducer by explicitly expressing the dependencies between components;\n\n<img src=\"https://img.alicdn.com/tfs/TB1oeXKJYPpK1RjSZFFXXa5PpXa-1976-568.png\" width=\"988px\" height=\"284px\">\n\n## Fish Redux provides a simple component abstract model\n\n> It is a combination of simple 3 functions\n\n<img src=\"https://img.alicdn.com/tfs/TB1vqB2J4YaK1RjSZFnXXa80pXa-900-780.png\" width=\"450px\" height=\"390px\">\n\n## Fish Redux provides an abstract component model of the Adapter\n\n> In addition to the underlying component model, Fish Redux provides an Adapter abstraction model to solve the performance problems of large cells on ListView.\n\n> Through the upper abstraction, we get the logical ScrollView, the performance of the ListView.\n\n<img src=\"https://img.alicdn.com/tfs/TB1x51VJ7PoK1RjSZKbXXX1IXXa-1852-612.png\" width=\"617px\" height=\"204px\">\n"
  },
  {
    "path": "docs/concept/widget-wrapper.md",
    "content": "### WidgetWrapper\n\n-   It is used to solve flutter's ui system, some Widgets that need to implement the featured interface, such as KeepAlive, because the Widget generated by Component will be wrapped by a Stateful Widget inside the fish redux framework.\n-   Sample Code\n\n```dart\nimport 'package:flutter/material.dart' hide Action;\n\nWidget repaintBoundaryWrapper(Widget widget) {\n  return RepaintBoundary(child: widget);\n}\n```\n"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n  <meta charset=\"UTF-8\">\r\n  <title>Document</title>\r\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\" />\r\n  <meta name=\"description\" content=\"Description\">\r\n  <meta name=\"viewport\" content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">\r\n  <link rel=\"stylesheet\" href=\"//unpkg.com/docsify/lib/themes/vue.css\">\r\n</head>\r\n<body>\r\n\r\n  <div id=\"app\"></div>\r\n  <script>\r\n    window.$docsify = {\r\n      name: '',\r\n      repo: '',\r\n      el: '#app',\r\n      repo: 'https://github.com/alibaba/fish-redux',\r\n\r\n      // 加载 _navbar.md\r\n      loadNavbar: true,\r\n      // 加载 _navbar.md\r\n      loadNavbar: '_navbar.md',\r\n\r\n      // 加载 _sidebar.md\r\n      loadSidebar: true,\r\n      // 加载 _sidebar.md\r\n      loadSidebar: '_sidebar.md',\r\n\r\n      // 完整配置参数\r\n      search: {\r\n        maxAge: 86400000, // 过期时间，单位毫秒，默认一天\r\n        paths: [], // or 'auto'\r\n        placeholder: 'Type to search',\r\n\r\n        // 支持本地化\r\n        placeholder: {\r\n          '/zh-cn/': '搜索',\r\n          '/': 'Type to search'\r\n        },\r\n\r\n        noData: 'No Results!',\r\n\r\n        // 支持本地化\r\n        noData: {\r\n          '/zh-cn/': '找不到结果',\r\n          '/': 'No Results'\r\n        },\r\n\r\n        // 搜索标题的最大程级, 1 - 6\r\n        depth: 2\r\n      },\r\n\r\n      plugins: [\r\n        function (hook) {\r\n          var footer = [\r\n            '<hr/>',\r\n            '<footer>',\r\n            '<span>Proudly published with <a href=\"https://github.com/docsifyjs/docsify\" target=\"_blank\">docsify</a>.</span>',\r\n            '</footer>'\r\n          ].join('')\r\n\r\n          hook.afterEach(function (html) {\r\n            return html + footer\r\n          })\r\n        }\r\n      ]\r\n\r\n    }\r\n  </script>\r\n  <script src=\"//unpkg.com/docsify/lib/docsify.min.js\"></script>\r\n  <script src=\"//unpkg.com/docsify/lib/plugins/zoom-image.js\"></script>\r\n</body>\r\n</html>\r\n"
  },
  {
    "path": "docs/zh-cn/README.md",
    "content": "\n-   **核心概念**\n    -   [Redux](/zh-cn/concept/redux.md)\n        -   [Action](/zh-cn/concept/action.md)\n        -   [Connector](/zh-cn/concept/connector.md)\n        -   [Reducer](/zh-cn/concept/reducer.md)\n        -   [Middleware](/zh-cn/concept/middleware.md)\n    -   [Component](/zh-cn/concept/component.md)\n        -   [View](/zh-cn/concept/view.md)\n        -   [Reducer](/zh-cn/concept/reducer.md)\n        -   [Effect](/zh-cn/concept/effect.md)\n        -   [HigherEffect](/zh-cn/concept/higher-effect.md)\n        -   [Lifecycle](/zh-cn/concept/lifecycle.md)\n        -   [Dependencies](/zh-cn/concept/dependencies.md)\n        -   [Dependent](/zh-cn/concept/dependent.md)\n        -   [ShouldUpdate](/zh-cn/concept/should-update.md)\n        -   [OnError](/zh-cn/concept/on-error.md)\n        -   [Filter](/zh-cn/concept/filter.md)\n        -   [OOP](/zh-cn/concept/oop.md)\n        -   [WidgetWrapper](/zh-cn/concept/widget-wrapper.md)\n        -   [Page](/zh-cn/concept/page.md)\n    -   [Adapter](/zh-cn/concept/adapter.md)\n        -   [StaticFlowAdapter](/zh-cn/concept/static-flow-adapter.md)\n        -   [DynamicFlowAdapter](/zh-cn/concept/dynamic-flow-adapter.md)\n        -   [CustomAdapter](/zh-cn/concept/custom-adapter.md)\n-   **其他**\n    -   [What's the difference between 'Fish Redux' and 'Redux' ?](/zh-cn/concept/what's-the-diiference.md)\n    -   [What's-adapter](/zh-cn/concept/what's-adapter.md)\n    -   [What's-connector](/zh-cn/concept/what's-connector.md)\n    -   [Mechanism](/zh-cn/concept/mechanism.md)\n    -   [Directory](/zh-cn/concept/directory.md)\n"
  },
  {
    "path": "docs/zh-cn/_sidebar.md",
    "content": "\n* [Redux](zh-cn/concept/redux.md)\n* [Action](zh-cn/concept/action.md)\n* [Connector](zh-cn/concept/connector.md)\n* [Reducer](zh-cn/concept/reducer.md)\n* [Middleware](zh-cn/concept/middleware.md)\n* [Component](zh-cn/concept/component.md)\n* [View](zh-cn/concept/view.md)\n* [Reducer](zh-cn/concept/reducer.md)\n* [Effect](zh-cn/concept/effect.md)\n* [HigherEffect](zh-cn/concept/higher-effect.md)\n* [Lifecycle](zh-cn/concept/lifecycle.md)\n* [Dependencies](zh-cn/concept/dependencies.md)\n* [Dependent](zh-cn/concept/dependent.md)\n* [ShouldUpdate](zh-cn/concept/should-update.md)\n* [OnError](zh-cn/concept/on-error.md)\n* [Filter](zh-cn/concept/filter.md)\n* [OOP](zh-cn/concept/oop.md)\n* [WidgetWrapper](zh-cn/concept/widget-wrapper.md)\n* [Page](zh-cn/concept/page.md)\n* [Adapter](zh-cn/concept/adapter.md)\n* [StaticFlowAdapter](zh-cn/concept/static-flow-adapter.md)\n* [DynamicFlowAdapter](zh-cn/concept/dynamic-flow-adapter.md)\n* [CustomAdapter](zh-cn/concept/custom-adapter.md)\n* [What's the difference between 'Fish Redux' and 'Redux' ?](zh-cn/concept/what's-the-diiference.md)\n* [What's-adapter](zh-cn/concept/what's-adapter.md)\n* [What's-connector](zh-cn/concept/what's-connector.md)\n* [Mechanism](zh-cn/concept/mechanism.md)\n* [Directory](zh-cn/concept/directory.md)\n"
  },
  {
    "path": "docs/zh-cn/concept/_navbar.md",
    "content": "* [En](/)\n* [中文](/zh-cn/)\n"
  },
  {
    "path": "docs/zh-cn/concept/_sidebar.md",
    "content": "\n* [Redux](zh-cn/concept/redux.md)\n* [Action](zh-cn/concept/action.md)\n* [Connector](zh-cn/concept/connector.md)\n* [Reducer](zh-cn/concept/reducer.md)\n* [Middleware](zh-cn/concept/middleware.md)\n* [Component](zh-cn/concept/component.md)\n* [View](zh-cn/concept/view.md)\n* [Reducer](zh-cn/concept/reducer.md)\n* [Effect](zh-cn/concept/effect.md)\n* [HigherEffect](zh-cn/concept/higher-effect.md)\n* [Lifecycle](zh-cn/concept/lifecycle.md)\n* [Dependencies](zh-cn/concept/dependencies.md)\n* [Dependent](zh-cn/concept/dependent.md)\n* [ShouldUpdate](zh-cn/concept/should-update.md)\n* [OnError](zh-cn/concept/on-error.md)\n* [Filter](zh-cn/concept/filter.md)\n* [OOP](zh-cn/concept/oop.md)\n* [WidgetWrapper](zh-cn/concept/widget-wrapper.md)\n* [Page](zh-cn/concept/page.md)\n* [Adapter](zh-cn/concept/adapter.md)\n* [StaticFlowAdapter](zh-cn/concept/static-flow-adapter.md)\n* [DynamicFlowAdapter](zh-cn/concept/dynamic-flow-adapter.md)\n* [CustomAdapter](zh-cn/concept/custom-adapter.md)\n* [What's the difference between 'Fish Redux' and 'Redux' ?](zh-cn/concept/what's-the-diiference.md)\n* [What's-adapter](zh-cn/concept/what's-adapter.md)\n* [What's-connector](zh-cn/concept/what's-connector.md)\n* [Mechanism](zh-cn/concept/mechanism.md)\n* [Directory](zh-cn/concept/directory.md)\n"
  },
  {
    "path": "docs/zh-cn/concept/action.md",
    "content": "# Action\n\n-   Action 包含两个字段\n    -   type\n    -   payload\n-   推荐的写法是\n    -   为一个组件|适配器创建一个 action.dart 文件，包含两个类\n        -   为 type 字段起一个枚举类\n        -   为 Action 的创建起一个 ActionCreator 类，这样利于约束 payload 的类型。\n    -   Effect 接受处理的 Action，以 on{Verb} 命名\n    -   Reducer 接受处理的 Action，以{verb} 命名\n    -   示例代码\n\n```dart\nenum MessageAction {\n    onShare,\n    shared,\n}\n\nclass MessageActionCreator {\n    static Action onShare(Map<String, Object> payload) {\n        return Action(MessageAction.onShare, payload: payload);\n    }\n\n    static Action shared() {\n        return const Action(MessageAction.shared);\n    }\n}\n```\n"
  },
  {
    "path": "docs/zh-cn/concept/adapter.md",
    "content": "# Adapter\n\n-   我们在基础 Component 的概念外，额外增加了一种组件化的抽象 Adapter。它的目标是解决 Component 模型在 ListView 的场景下的 3 个问题\n    -   1）将一个\"Big-Cell\"放在 ListView 里，无法享受 ListView 代码的性能优化。\n    -   2）Component 无法区分 appear|disappear 和 init|dispose 事件。\n    -   3）Effect 的生命周期和 View 的耦合，在 ListView 的有些场景下不符合直观的预期。\n-   一个 Adapter 和 Component 几乎都是一致的，除了以下几点\n    -   Component 生成一个 Widget，Adapter 生成一个 ListAdapter，ListAdapter 有能力生成一组 Widget。\n        -   不具体生成 Widget，而是一个 ListAdapter，能非常大的提升页面帧率和流畅度。\n    -   Effect-Lifecycle-Promote\n        -   Component 的 Effect 是跟着 Widget 的生命周期走的，Adapter 的 Effect 是跟着上一级的 Widget 的生命周期走。\n        -   Effect​ 提升，极大的解除了业务逻辑和视图生命的耦合，即使它的展示还未出现，的其他模块依然能通过 dispatch-api，调用它的能力。\n    -   appear|disappear 的通知\n        -   由于 Effect 生命周期的提升，我们就能更加精细的区分 init|dispose 和 appear|disappear。而这在 Component 的模型中是无法区分的。\n    -   Reducer is long-lived, Effect is medium-lived, View is short-lived.\n-   Adapter 的三种实现\n    -   [DynamicFlowAdapter](dynamic-flow-adapter-cn.md)\n    -   [StaticFlowAdapter](static-flow-adapter-cn.md)\n    -   [CustomAdapter](custom-adapter-cn.md)\n"
  },
  {
    "path": "docs/zh-cn/concept/auto-dispose.md",
    "content": "# Auto-Dispose\n\n-   它是一个非常简易管理生命周期对象的方式。一个 auto-dispose 对象可以自我主动释放，或者在它 follow 的 托管对象释放的时候，释放。\n-   在 Effect 中使用的 Context，以及 HigherEffect 中的 EffectPart，都是 auto-dispose 对象。所以我们可以方便的将自定义的需要做生命周期管理的对象托管给它们。\n-   示例代码\n\n```dart\nclass ItemWidgetBindingObserver extends WidgetsBindingObserver\n    with AutoDispose {\n  ItemWidgetBindingObserver() : super() {\n    WidgetsBinding.instance.addObserver(this);\n  }\n\n  @override\n  void didChangeAppLifecycleState(AppLifecycleState state) {\n    if (AppConfig.flutterBinding.framesEnabled &&\n        state == AppLifecycleState.resumed) {\n      AppConfig.flutterBinding.performReassemble();\n    }\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n    WidgetsBinding.instance.removeObserver(this);\n  }\n}\n\nvoid _init(Action action, Context<ItemPageContainerState> ctx) {\n    final ItemWidgetBindingObserver observer = ItemWidgetBindingObserver();\n    observer.follow(ctx);\n}\n\n```\n"
  },
  {
    "path": "docs/zh-cn/concept/component.md",
    "content": "# Component\n\n组件是对视图展现和逻辑功能的封装。\n面向当下，从 Redux 的视角看，我们对组件分为状态修改的功能(Reducer)和其他。\n面向未来，从 UI-Automation 的视角看，我们对组件分为展现表达和其他。\n结合上面两个视角，于是我们得到了，View、 Effect、Reducer 三部分，称之为组件的三要素，分别负责了组件的展示、非修改数据的行为、修改数据的操作。\n\n我们以显式配置的方式来完成大组件所依赖的小组件、适配器的注册，这份依赖配置称之为 Dependencies。\n\n所以有了这个公式\nComponent = View + Effect(可选) + Reducer(可选) + Dependencies(可选)\n\n分治：从组件的角度\n<img src=\"https://img.alicdn.com/tfs/TB1vqB2J4YaK1RjSZFnXXa80pXa-900-780.png\" width=\"450px\" height=\"390px\">\n\n集中：从 Store 的角度\n<img src=\"https://img.alicdn.com/tfs/TB1sThMJYvpK1RjSZFqXXcXUVXa-1426-762.png\" width=\"713px\" height=\"381px\">\n"
  },
  {
    "path": "docs/zh-cn/concept/connector.md",
    "content": "# Connector<T, P>\n\n-   它表达了如何从一个大数据中读取小数据，同时对小数据的修改如何同步给大数据，这样的数据连接关系。\n-   它是将一个集中式的 Reducer，可以由多层次多模块的小 Reducer 自动拼装的关键。\n    -   它大大降低了我们使用 Redux 的复杂度。我们不再关心组装过程，我们关心的核心是什么动作促使数据怎么变化。\n-   它使用在配置 Dependencies 中，在配置中我们就固化了大组件和小组件之间的连接关系(数据管道)，所以在我们使用小组件的时候是不需要传入任何动态参数的。\n-   ![image.png | left | 719x375](https://cdn.nlark.com/lark/0/2018/png/82574/1545365202743-01074be7-f067-45c7-aae0-91b12cd50ae6.png)\n\n-   Sample Code\n\n```dart\nclass DetialState {\n    Profile profile;\n    String message;\n}\n\nConnOp<DetialState, String> messageConnector() {\n    return ConnOp<DetialState, String>(\n        get: (DetialState state) => state.message,\n        set: (DetialState state, String message) => state.message = message,\n    );\n}\n```\n"
  },
  {
    "path": "docs/zh-cn/concept/custom-adapter.md",
    "content": "# CustomAdapter\n\n-   对大 Cell 的自定义实现\n-   要素和 Component 类似，不一样的地方是 Adapter 的视图部分返回的是一个 ListAdapter\n-   示例代码\n\n```dart\nclass CommentAdapter extends Adapter<CommentState> {\n    CommentAdapter()\n        : super(\n            adapter: buildCommentAdapter,\n            effect: buildCommentEffect(),\n            reducer: buildCommentReducer(),\n        );\n}\n\nListAdapter buildCommentAdapter(CommentState state, Dispatch dispatch, ViewService service) {\n    final List<IndexedWidgetBuilder> builders = Collections.compact(<IndexedWidgetBuilder>[]\n    ..add((BuildContext buildContext, int index) =>\n        _buildDetailCommentHeader(state, dispatch, service))\n    ..addAll(_buildCommentViewList(state, dispatch, service))\n    ..add(isEmpty(state.commentListRes?.items)\n        ? (BuildContext buildContext, int index) =>\n            _buildDetailCommentEmpty(state.itemInfo, dispatch)\n        : null)\n    ..add(state.commentListRes?.getHasMore() == true\n        ? (BuildContext buildContext, int index) => _buildLoadMore(dispatch)\n        : null));\n    return ListAdapter(\n    (BuildContext buildContext, int index) =>\n        builders[index](buildContext, index),\n    builders.length,\n    );\n}\n\n///builds\n```\n"
  },
  {
    "path": "docs/zh-cn/concept/dependencies.md",
    "content": "# Dependencies\n\n-   Dependencies 是一个表达组件之间依赖关系的结构。它接收两个字段\n    -   slots\n        -   <String, [Dependent](dependent-cn.md)>{}\n    -   [adapter](adapter-cn.md)\n-   它主要包含三方面的信息\n    -   slots，组件依赖的插槽。\n    -   adapter，组件依赖的具体适配器（用来构建高性能的 ListView）。\n    -   [Dependent](dependent-cn.md) 是 subComponent | subAdapter + [connector](connector-cn.md) 的组合。\n    -   一个 组件的 [Reducer](reducer-cn.md) 由 Component 自身配置的 Reducer 和它的 Dependencies 下的所有子 Reducers 自动复合而成。\n-   示例代码\n\n```dart\n///register in component\nclass ItemComponent extends ItemComponent<ItemState> {\n  ItemComponent()\n      : super(\n          view: buildItemView,\n          reducer: buildItemReducer(),\n          dependencies: Dependencies<ItemState>(\n            slots: <String, Dependent<ItemState>>{\n              'appBar': AppBarComponent().asDependent(AppBarConnector()),\n              'body': ItemBodyComponent().asDependent(ItemBodyConnector()),\n              'ad_ball': ADBallComponent().asDependent(ADBallConnector()),\n              'bottomBar': BottomBarComponent().asDependent(BottomBarConnector()),\n            },\n          ),\n        );\n}\n\n///call in view\nWidget buildItemView(ItemState state, Dispatch dispatch, ViewService service) {\n  return Scaffold(\n      body: Stack(\n        children: <Widget>[\n          service.buildComponent('body'),\n          service.buildComponent('ad_ball'),\n          Positioned(\n            child: service.buildComponent('bottomBar'),\n            left: 0.0,\n            bottom: 0.0,\n            right: 0.0,\n            height: 100.0,\n          ),\n        ],\n      ),\n      appBar: AppbarPreferSize(child: service.buildComponent('appBar')));\n}\n```\n"
  },
  {
    "path": "docs/zh-cn/concept/dependent.md",
    "content": "### Dependent\n\n-   Dependent = connector<T, P> + subComponent | subAdapter 的组合，它表达了小组件|小适配器是如何连接到 Component 的。\n-   示例代码\n\n```dart\n/// todo\n```\n"
  },
  {
    "path": "docs/zh-cn/concept/directory.md",
    "content": "# Directory\n\n推荐的目录结构会是这样\n\n```\nsample_page\n    -- action.dart /// define action types and action creator\n    -- page.dart /// config a page or component\n    -- view.dart /// define a function which expresses the presentation of user interface\n    -- effect.dart /// define a function which handles the side-effect\n    -- reducer.dart /// define a function which handles state-change\n    -- state.dart /// define a state and some connector of substate\n    components\n        sample_component\n        -- action.dart\n        -- component.dart\n        -- view.dart\n        -- effect.dart\n        -- reducer.dart\n        -- state.dart\n```\n\n上层负责组装，下层负责实现。\n"
  },
  {
    "path": "docs/zh-cn/concept/dynamic-flow-adapter.md",
    "content": "# DynamicFlowAdapter\n\n-   模版是一个 Map，接受一个数组类型的数据驱动\n-   示例代码\n\n```dart\nclass RecommendAdapter extends DynamicFlowAdapter<RecommendState> {\n    RecommendAdapter()\n        : super(\n            pool: <String, Component<Object>>{\n                'card_0': RecommendTitleComponent(),\n                'card_1': RecommendRowComponent(),\n            },\n            connector: RecommendCardListConnector(),\n        );\n}\n```\n<img src=\"https://img.alicdn.com/tfs/TB10lxHLMDqK1RjSZSyXXaxEVXa-1838-1024.png\" width=\"919px\" height=\"512px\">\n"
  },
  {
    "path": "docs/zh-cn/concept/effect.md",
    "content": "# Effect\n\nEffect顾名思义，用于处理Action的副作用。\n\n我估摸着有人就要问我了，副作用是啥玩意？\n\n打个比方吧，假如我拥有一个函数 `f()`\n\n```text\nfn f(x):\n  return x * 1\n```\n\n此时此刻，另一个函数 `g()`\n\n```text\nfn g(x):\n  changeSystemEntropy()\n  return ax ^ 2 + bx + c\n```\n\n我们可以发现，`g()`里边有个改变系统熵的行为。这在函数式编程思想中，就叫做副作用，因为它可能影响到除了这个函数内部自身状态以外的其他状态。\n\n在Fish-Redux中同样，我们通过 `dispatch()` 一些action实现状态修改，但是相对于状态来说，对外部的操作，类似于 `SystemChrome.setSystemUIOverlayStyle()`这样的操作，都是副作用。\n\n现在介绍完了副作用，也没啥可介绍的了。\n\nEffect用法跟Reducer差不太多，但是作用完全不同。\n\n除了上面介绍的场景之外，异步请求也是一个经常会有的情况，这时候Effect可以帮你方便的解决这些问题。\n\n你可以通过控制effect的返回值来达到某些目的，默认情况下，effect会在reducer之前被执行。\n\n当前effect返回 `true` 的时候，就会停止后续的effect和reducer的操作\n\n当前effect返回 `false` 的时候，后续effect和reducer继续执行\n\n-   Effect 是一个处理所有副作用的函数。它接收下面的参数\n    -   Action action\n    -   Context context\n        -   BuildContext context\n        -   T state\n        -   dispatch\n        -   isDisposed\n        \nEffect会接收来自 View 的“意图”，包括对应的生命周期的回调，然后做出具体的执行。\n    -   它的处理可能是一个异步函数，数据可能在过程中被修改，所以我们应该通过 context.state 获取最新数据。\n    -   如果它要修改数据，应该发一个 Action 到 Reducer 里去处理。它对数据是只读的，不能直接去修改数据。\n    -   如果它的返回值是一个非空值，则代表自己优先处理，不再做下一步的动作；否则广播给其他组件的 Effect 部分，同时发送给 Reducer。\n\n> Self-First-Broadcast。\n> ![image.png | left | 747x399](https://cdn.nlark.com/lark/0/2018/png/82574/1545365233153-4c8105b4-050c-49e6-be02-dbf28a861caa.png)\n\n-   示例代码\n\n```dart\n/// one style of writing\nFutureOr<Object> sideEffect(Action action, Context<String> ctx) async {\n  if (action.type == Lifecycle.initState) {\n    //do something on initState\n    return true;\n  } else if (action.type == 'onShare') {\n    //do something on onShare\n    await Future<void>.delayed(Duration(milliseconds: 1000));\n    ctx.dispatch(const Action('shared'));\n    return true;\n  }\n  return null;\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: sideEffect,\n        );\n}\n```\n\n```dart\n/// another style of writing\nEffect<String> buildEffect() {\n  return combineEffects(<Object, Effect<String>>{\n    Lifecycle.initState: _initState,\n    'onShare': _onShare,\n  });\n}\n\nvoid _initState(Action action, Context<String> ctx) {\n  //do something on initState\n}\n\nvoid _onShare(Action action, Context<String> ctx) async {\n  //do something on onShare\n  await Future<void>.delayed(Duration(milliseconds: 1000));\n  ctx.dispatch(const Action('shared'));\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n        );\n}\n```\n"
  },
  {
    "path": "docs/zh-cn/concept/filter.md",
    "content": "# Filter\n\n-   Filter 是用来优化 Reducer 的性能的。因为 Reducer 是层层组装的，所以处理每一个 Action，理论上会遍历一遍所有的小 Reducer，在一些非常复杂的场景下，这样的一次深度遍历的耗时可能会到毫秒级别（一般情况下都应该小于 1 毫秒）。那么我们需要对 Reducer 做性能优化，提前决定要不要遍历这份 Reducer 子树，减少遍历的深度和次数。\n-   示例代码\n\n```dart\nbool filter(Action action) {\n    return action.type == 'some action';\n}\n```\n"
  },
  {
    "path": "docs/zh-cn/concept/higher-effect.md",
    "content": "# HigherEffect\n\n-   由于 Effect 有可能有自己一些临时状态（尽管不建议这么做，但还是提供了支持），为了支持这个特性，我们将 Effect 提升为高阶函数，将它的状态放在闭包里。\n-   框架支持 Effect|HigherEffect 的配置，但是不能对一个组件或适配器同时都配置，那样会带来困扰，一般情况下，都配置往往是个显式的疏忽大意。\n-   HigherEffect = (Context ctx) => (Action action) => FutureOr\n-   更详细的例子请参考 [OOP](oop-cn.md) - EffectPart\n"
  },
  {
    "path": "docs/zh-cn/concept/lifecycle.md",
    "content": "# Lifecycle\n\n-   默认的所有生命周期，本质上都来自于 flutter State<StatefulWidget> 中的生命周期。\n    -   initState\n    -   didChangeDependencies\n    -   build\n    -   didUpdateWidget\n    -   deactivate\n    -   dispose\n-   在组件内，Reducer 的生命周期是和页面一致的，Effect 和 View 的生命周期是和组件的 Widget 一致的。\n-   在适配器中，Reducer 的生命周期是和页面一致的，Effect 的生命周期是和 ListView 的生命周期一致，View 的生命周期是短暂的(划入不可见区域即销毁)。同时增加了 appear 和 disappear 的生命周期， 代表这个 adapter 管理的视图数组，刚进入显示区和完全离开显示区的回调。\n"
  },
  {
    "path": "docs/zh-cn/concept/mechanism.md",
    "content": "# Communication Mechanism\n\n## 页面内通信\n\n-   组件|适配器内通信\n-   组件|适配器间内通信\n\n![image.png | left | 747x399](https://cdn.nlark.com/lark/0/2018/png/82574/1545365233153-4c8105b4-050c-49e6-be02-dbf28a861caa.png)\n\nSelf-First-Broadcast。\n发出的 Action，自己优先处理，否则广播给其他组件和 Redux 处理。\n\n最终我们通过一个简单而直观的 dispatch 完成了组件内，组件间（父到子，子到父，兄弟间等）的通信。\n\n## 页面间通信\n\n-   页面间通信\n    -   Context.appBroadcast\n        -   每一个页面的 PageStore 都会收到消息，各自独立负责处理。\n\n![image.png | left | 691x519](https://cdn.nlark.com/lark/0/2018/png/82574/1545368705599-745c46a3-f5c6-41a7-a757-1bc6f9a389d4.png)\n\n# Refresh Mechanism\n\n## 数据刷新\n\n-   局部数据修改，自动层层触发上层数据的浅拷贝，对业务代码是透明的。\n-   层层的数据的拷贝\n    -   一方面是对 Redux 数据修改的严格的 follow。\n    -   另一方面也是对数据驱动展示的严格的 follow。\n        -   数据的任何一个局部的变动，必须要让能看到这个局部的所有视图感知到。如果不拷贝，对应的视图通过新旧两份数据的比较（同一个引用），会错以为自己没有发生变化。\n\n![image.png | left | 747x361](https://cdn.nlark.com/lark/0/2018/png/82574/1545386668521-0081cb5f-8017-47d1-ad7c-8802bb0be8a0.png)\n\n## 视图刷新\n\n-   扁平化通知到所有组件，组件通过 shouldUpdate 确定自己是否需要刷新\n\n![image.png | left | 747x336](https://cdn.nlark.com/lark/0/2018/png/82574/1545386773247-2eddfa99-e6b9-4be9-ac43-d1944ff44e9b.png)\n"
  },
  {
    "path": "docs/zh-cn/concept/middleware.md",
    "content": "# Middleware\n\n关于 Middleware 的定义、签名和 ReduxJS 社区是一致的。\n\n示例代码\n\n```dart\nMiddleware<T> logMiddleware<T>({\n  String tag = 'redux',\n  String Function(T) monitor,\n}) {\n  return ({Dispatch dispatch, Get<T> getState}) {\n    return (Dispatch next) {\n      return isDebug()\n          ? (Action action) {\n              print('---------- [$tag] ----------');\n              print('[$tag] ${action.type} ${action.payload}');\n\n              final T prevState = getState();\n              if (monitor != null) {\n                print('[$tag] prev-state: ${monitor(prevState)}');\n              }\n\n              next(action);\n\n              final T nextState = getState();\n              if (monitor != null) {\n                print('[$tag] next-state: ${monitor(nextState)}');\n              }\n\n              if (prevState == nextState) {\n                print('[$tag] warning: ${action.type} has not been used.');\n              }\n\n              print('========== [$tag] ================');\n            }\n          : next;\n    };\n  };\n}\n```\n\n更多的参考 src/utils/common_middleware\n"
  },
  {
    "path": "docs/zh-cn/concept/on-error.md",
    "content": "# OnError\n\n-   集中处理由 Effect 产生的业务异常，无论是同步函数还是异步函数。有了统一的异常处理机制，我们就能站在一个更高的抽象角度，对业务代码做出合理的简化。\n-   示例代码\n\n```dart\nbool onMessageError(Exception e, Context<String> ctx) {\n    if(e is BizException) {\n        ///do some toast\n        return true;\n    }\n    return false;\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n            reducer: buildMessageReducer(),\n            onError: onMessageError,\n        );\n}\n```\n"
  },
  {
    "path": "docs/zh-cn/concept/oop.md",
    "content": "# OOP\n\n-   虽然框架推荐使用的函数式的编程方式，也提供面向对象式的编程方式的支持。\n    -   ViewPart\n        -   需要复写 build 函数。\n        -   需要的 state，dispatch，viewService 的参数，已经成为了对象的字段可以直接使用。\n        -   它是@immutable 的，所以不应该也不需要在内部定义可变字段。\n    -   EffectPart\n        -   需要复写 createMap 函数。\n        -   需要的 Context 已经被打平，作为了对象的字段可以直接使用。\n        -   可以定义字段，它的可见性也仅限于自身。\n        -   它必须配合 higherEffect 一起使用。\n-   示例代码\n\n```dart\nclass MessageView extends ViewPart<MessageState> {\n    @override\n    Widget build() {\n        return Column(children: [\n            viewService.buildComponent('profile'),\n            InkWell(\n                child: Text('$message'),\n                onTap: () => dispatch(const Action('onShare')),\n            ),\n        ]);\n    }\n}\n\nclass MessageEffect extends EffectPart<MessageState> {\n    ///we could put some Non-UI fields here.\n\n    @override\n    Map<Object, OnAction> createMap() {\n        return <Object, OnAction>{\n            Lifecycle.initState: _initState,\n            'onShare': _onShare,\n        };\n    }\n\n    void _initState(Action action) {\n        //do something on initState\n    }\n\n    void _onShare(Action action) async {\n        //do something on onShare\n        await Future<void>.delayed(Duration(milliseconds: 1000));\n        dispatch(const Action('shared'));\n    }\n}\n\nclass MessageComponent extends Component<MessageState> {\n    MessageComponent(): super(\n        view: MessageView().asView(),\n        higherEffect: higherEffect(() => MessageEffect()),\n    );\n}\n```\n"
  },
  {
    "path": "docs/zh-cn/concept/page.md",
    "content": "# Page\n\n-   一个页面内都有且仅有一个 Store\n-   Page 继承于 Component，所以它能配置所有 Component 的要素\n-   Page 能配置 Middleware，用于对 Redux 做 AOP 管理\n-   Page 必须配置一个初始化页面数据的初始化函数  initState\n    <img src=\"https://img.alicdn.com/tfs/TB1ASfDJ9zqK1RjSZFHXXb3CpXa-1636-756.png\" width=\"818px\" height=\"378px\">\n\n-   示例代码\n\n```dart\n/// Hello World\nclass HelloWordPage extends Page<String, String> {\n    HelloWordPage():\n        super(\n            initState: (String msg) => msg,\n            view:(String msg, _, __) => Text('Hello ${msg}'),\n        );\n}\n\nHelloWordPage().buildPage('world')\n```\n"
  },
  {
    "path": "docs/zh-cn/concept/reducer.md",
    "content": "# Reducer\n\n-   Reducer 是一个上下文无关的 pure function。它接收下面的参数\n    -   T state\n    -   Action action\n-   它主要包含三方面的信息\n    -   接收一个“意图”， 做出数据修改。\n    -   如果要修改数据，需要创建一份新的拷贝，修改在拷贝上。\n    -   如果数据修改了，它会自动触发 State 的层层数据的拷贝，再以扁平化方式通知组件刷新。\n-   示例代码\n\n```dart\n/// one style of writing\nString messageReducer(String msg, Action action) {\n  if (action.type == 'shared') {\n    return '$msg [shared]';\n  }\n  return msg;\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n            reducer: messageReducer,\n        );\n}\n```\n\n```dart\n/// another style of writing\nReducer<String> buildMessageReducer() {\n  return asReducer(<Object, Reducer<String>>{\n    'shared': _shared,\n  });\n}\n\nString _shared(String msg, Action action) {\n  return '$msg [shared]';\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n            effect: buildEffect(),\n            reducer: buildMessageReducer(),\n        );\n}\n```\n\n> 推荐的是第二种写法\n"
  },
  {
    "path": "docs/zh-cn/concept/redux.md",
    "content": "### Redux\n\n-   State\n-   [Action](action-cn.md)\n-   [Reducer](reducer-cn.md)\n-   Store\n-   [Middleware](middleware-cn.md)\n\n以上概念和社区的 Redux 是完全一致的。\nRedux 是一个用来做[可预测][集中式][易调试][灵活性]的数据管理的框架。\n如果想对 Redux 有更近一步的理解，请参考 [https://github.com/reduxjs/redux](https://github.com/reduxjs/redux)\n"
  },
  {
    "path": "docs/zh-cn/concept/should-update.md",
    "content": "# ShouldUpdate\n\n-   当数据发生变更，Store 扁平化地通知所有组件\n-   框架默认使用 identical 比较新旧两份数据来决定是否需要刷新。\n-   如果我们对组件的刷新会有非常精确化的诉求， 那么我们可以自己定义一个 ShouldUpdate。\n-   示例代码\n\n```dart\nbool shouldUpdate(DetailState old, DetailState now) {\n    return old.message != now.message;\n}\n```\n"
  },
  {
    "path": "docs/zh-cn/concept/static-flow-adapter.md",
    "content": "# StaticFlowAdapter\n\n-   模版是一个 Array，接受 Object|Map 的数据驱动。\n-   模版接收一个 Dependent 的数组，每一个 Dependent 可以是 Component 或者 Adapter + Connector<T,P> 的组合。\n-   抽象地看，它非常的像是一个 flatMap + compact 的操作。\n-   示例代码\n\n```dart\nclass ItemBodyComponent extends Component<ItemBodyState> {\n    ItemBodyComponent()\n        : super(\n            view: buildItemBody,\n            dependencies: Dependencies<ItemBodyState>(\n            adapter: StaticFlowAdapter<ItemBodyState>(\n                slots: <Dependent<ItemBodyState>>[\n                    VideoAdapter().asDependent(videoConnector()),\n                    UserInfoComponent().asDependent(userInfoConnector()),\n                    DescComponent().asDependent(descConnector()),\n                    ItemImageComponent().asDependent(itemImageConnector()),\n                    OriginDescComponent().asDependent(originDescConnector()),\n                    VisitComponent().asDependent(visitConnector()),\n                    SameMoreComponent().asDependent(sameMoreConnector()),\n                    PondComponent().asDependent(pondConnector()),\n                    CommentAdapter().asDependent(commentConnector()),\n                    RecommendAdapter().asDependent(recommendConnector()),\n                    PaddingComponent().asDependent(paddingConnector()),\n                ]),\n            ),\n        );\n}\n\n```\n\n<img src=\"https://img.alicdn.com/tfs/TB1sXXOLQvoK1RjSZPfXXXPKFXa-1666-1104.png\" width=\"833px\" height=\"552px\">\n"
  },
  {
    "path": "docs/zh-cn/concept/view.md",
    "content": "# View\n\n-   View 是一个输出 Widget 的上下文无关的函数。它接收下面的参数\n    -   T state\n    -   Dispatch\n    -   ViewService\n-   它主要包含三方面的信息\n    -   视图完全由数据驱动。\n    -   视图产生的事件／回调，通过 Dispatch 发出“意图”，但绝不做具体的实现。\n    -   使用依赖的组件／适配器，通过在组件上显示配置，再通过 ViewService 标准化调用。\n        -   其中 ViewService 提供了三个能力\n            -   BuildContext context，获取 flutter Build-Context 的能力\n            -   Widget buildView(String name), 直接创建子组件的能力\n                -   这里传入的 name 即在 Dependencies 上配置的名称。\n                -   创建子组件不需要传入任何其他的参数，因为子组件需要的参数，已经通过 Dependencies 配置中，将它们的数据关系，通过 connector 确立。\n            -   ListAdapter buildAdapter()， 直接创建适配器的能力\n-   示例代码\n\n```dart\nWidget buildMessageView(String message, Dispatch dispatch, ViewService viewService) {\n  return Column(children: [\n    viewService.buildComponent('profile'),\n    InkWell(\n      child: Text('$message'),\n      onTap: () => dispatch(const Action('onShare')),\n    ),\n  ]);\n}\n\nclass MessageComponent extends Component<String> {\n    MessageComponent(): super(\n            view: buildMessageView,\n        );\n}\n```\n"
  },
  {
    "path": "docs/zh-cn/concept/what's-adapter.md",
    "content": "# What's adapter\n\n面向 ListView 场景的分治设计 Adapter。\n\n> 在解答什么是 adapter 之前，我们来看下一般框架对 ListView 的分治是怎么做的。传统的手段，我们对 ListView 的分治更多的局限于它展现部分，而它的逻辑部分往往是集中的。而当我们试图将 ListView 下的某一局部的展现和逻辑封装在一起，我们就会遇到\"Big-Cell\"问题，面临性能的显著降低。\n> 这里面存在一个分治和性能上的矛盾。这个矛盾带来了复用难，可维护差，难以协作等中大型场景下的问题。\n>\n> 解决这个问题，有两种思路：\n>\n> 1. 下沉到 UI 表达层（Widgets），去实现一个高性能的 ScrollView。\n> 2. 向上做模型抽象，得到一个逻辑上的 ScrollView，性能上的 ListView。\n>\n> fish redux 选择了第二条更加通用的路径来解决 LisView 下的分治问题。\n>\n> 一个 ListView 对应了一个 Adapter，这看上去非常的像 Android 里的设计，但事实上 fish-redux 里的 Adapter 概念走的更远。\n>\n> 1. 一个 Adapter 是可以由多个 Component 和 Adapter 组合而成，它有点像 flatmap & compact 的 api 的叠加。\n> 2. Adapter 以及它的子 Adapter 的生命周期是和 ListView 是等效的。它像跨斗一般附着于 ListView 的生命周期之上。同时由于 Adapter 生命周期的提升，我们额外收获了两个非常有用的事件消息(appear & disappear)。\n>\n> > 注意 ⚠️ 在 Adapter 里配置的子 Component，它的生命周期和它所对应的 WidgetState 是一致的，所以它的是短暂的。\n\n-   Adapter 的容器有两类，用图来说明吧：\n\n<img src=\"https://img.alicdn.com/tfs/TB1sXXOLQvoK1RjSZPfXXXPKFXa-1666-1104.png\" width=\"833px\" height=\"552px\">\n\n<img src=\"https://img.alicdn.com/tfs/TB10lxHLMDqK1RjSZSyXXaxEVXa-1838-1024.png\" width=\"919px\" height=\"512px\">\n"
  },
  {
    "path": "docs/zh-cn/concept/what's-connector.md",
    "content": "# What's connector\n\n在解答 connector 是什么之前，我们来先看一个代码片段\n\n```javascript\nlet hasChanged = false;\nconst nextState = {};\nfor (let i = 0; i < finalReducerKeys.length; i++) {\n\tconst key = finalReducerKeys[i];\n\tconst reducer = finalReducers[key];\n\tconst previousStateForKey = state[key];\n\tconst nextStateForKey = reducer(previousStateForKey, action);\n\tif (typeof nextStateForKey === 'undefined') {\n\t\tconst errorMessage = getUndefinedStateErrorMessage(key, action);\n\t\tthrow new Error(errorMessage);\n\t}\n\tnextState[key] = nextStateForKey;\n\thasChanged = hasChanged || nextStateForKey !== previousStateForKey;\n}\nreturn hasChanged ? nextState : state;\n```\n\n以上来自于 Reduxjs-[combineReducers](https://github.com/reduxjs/redux/blob/master/src/combineReducers.js)的核心实现。\n\ncombineReducers 是一个将 Reducer 分治的函数，让一个庞大数据的 Reducer 可以由多层的更小的 Reducer 组合而成。\n\n这是 Redux 框架里的核心 API，但是他有缺点。他有非常明显的语言的局限性，如下 3 点:\n\n1. 浅拷贝一个任意对象\n\n```javascript\nconst nextState = {};\n```\n\n2. 读取字段\n\n```javascript\nconst previousStateForKey = state[key];\n```\n\n3. 写入字段\n\n```javascript\nnextState[key] = nextStateForKey;\n```\n\n将上面的 3 点抽象来看：\n\n1. State 的 clone 的能力（浅拷贝）\n2. Get & Set 的能力，即为 Connector 的概念。\n\n有了 以上两点，我们才完全集成了 Redux 的所有精华，同时将它的设计更上一个通用的维度。\n"
  },
  {
    "path": "docs/zh-cn/concept/what's-the-diiference.md",
    "content": "# What's different with Redux ?\n\n## 它们是解决不同层面问题的两个框架\n\n> Redux 是一个专注于状态管理的框架；Fish Redux 是基于 Redux 做状态管理的应用框架。\n\n> 应用框架不仅仅要解决状态管理的问题，还要解决分治，通信，数据驱动，解耦等等问题。\n\n## Fish Redux 解决了集中和分治的矛盾。\n\n> Redux 通过使用者手动组织代码的形式来完成从小的 Reducer 到主 Reducer 的合并过程；\n\n> Fish Redux 通过显式的表达组件之间的依赖关系，由框架自动完成从细力度的 Reducer 到主 Reducer 的合并过程；\n\n<img src=\"https://img.alicdn.com/tfs/TB1oeXKJYPpK1RjSZFFXXa5PpXa-1976-568.png\" width=\"988px\" height=\"284px\">\n\n## Fish Redux 提供了一个简单的组件抽象模型\n\n> 它通过简单的 3 个函数组合而成\n\n<img src=\"https://img.alicdn.com/tfs/TB1vqB2J4YaK1RjSZFnXXa80pXa-900-780.png\" width=\"450px\" height=\"390px\">\n\n## Fish Redux 提供了一个 Adapter 的抽象组件模型\n\n> 在基础的组件模型以外，Fish Redux 提供了一个 Adapter 抽象模型，用来解决在 ListView 上大 Cell 的性能问题。\n\n> 通过上层抽象，我们得到了逻辑上的 ScrollView，性能上的 ListView。\n\n<img src=\"https://img.alicdn.com/tfs/TB1x51VJ7PoK1RjSZKbXXX1IXXa-1852-612.png\" width=\"617px\" height=\"204px\">\n"
  },
  {
    "path": "docs/zh-cn/concept/widget-wrapper.md",
    "content": "### WidgetWrapper\n\n-   它用来解决 flutter 的 ui 体系下，一些需要实现特色接口的 Widget，比如 KeepAlive，因为通过 Component 产生的 Widget 会被一个框架内部的 Stateful 的 Widget 所包裹。\n-   示例代码\n\n```dart\nimport 'package:flutter/material.dart' hide Action;\n\nWidget repaintBoundaryWrapper(Widget widget) {\n  return RepaintBoundary(child: widget);\n}\n```\n"
  },
  {
    "path": "example/.flutter-plugins-dependencies",
    "content": "{\"info\":\"This is a generated file; do not edit or check into version control.\",\"plugins\":{\"ios\":[{\"name\":\"path_provider\",\"path\":\"/Users/linyunhe/.pub-cache/hosted/pub.dartlang.org/path_provider-0.4.1/\",\"dependencies\":[]}],\"android\":[{\"name\":\"path_provider\",\"path\":\"/Users/linyunhe/.pub-cache/hosted/pub.dartlang.org/path_provider-0.4.1/\",\"dependencies\":[]}],\"macos\":[],\"linux\":[],\"windows\":[],\"web\":[]},\"dependencyGraph\":[{\"name\":\"path_provider\",\"dependencies\":[]}],\"date_created\":\"2021-06-08 17:37:57.339650\",\"version\":\"1.22.6-xianyu\"}"
  },
  {
    "path": "example/.gitignore",
    "content": "# Miscellaneous\n*.class\n*.lock\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# Visual Studio Code related\n.vscode/\n\n# Flutter/Dart/Pub related\n**/doc/api/\n.dart_tool/\n.flutter-plugins\n.packages\n.pub-cache/\n.pub/\nbuild/\n\n# Android related\n**/android/**/gradle-wrapper.jar\n**/android/.gradle\n**/android/captures/\n**/android/gradlew\n**/android/gradlew.bat\n**/android/local.properties\n**/android/**/GeneratedPluginRegistrant.java\n\n# iOS/XCode related\n**/ios/**/*.mode1v3\n**/ios/**/*.mode2v3\n**/ios/**/*.moved-aside\n**/ios/**/*.pbxuser\n**/ios/**/*.perspectivev3\n**/ios/**/*sync/\n**/ios/**/.sconsign.dblite\n**/ios/**/.tags*\n**/ios/**/.vagrant/\n**/ios/**/DerivedData/\n**/ios/**/Icon?\n**/ios/**/Pods/\n**/ios/**/.symlinks/\n**/ios/**/profile\n**/ios/**/xcuserdata\n**/ios/.generated/\n**/ios/Flutter/App.framework\n**/ios/Flutter/Flutter.framework\n**/ios/Flutter/Generated.xcconfig\n**/ios/Flutter/app.flx\n**/ios/Flutter/app.zip\n**/ios/Flutter/flutter_assets/\n**/ios/ServiceDefinitions.json\n**/ios/Runner/GeneratedPluginRegistrant.*\n\n# Exceptions to above rules.\n!**/ios/**/default.mode1v3\n!**/ios/**/default.mode2v3\n!**/ios/**/default.pbxuser\n!**/ios/**/default.perspectivev3\n!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages\n"
  },
  {
    "path": "example/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b\n  channel: stable\n\nproject_type: app\n"
  },
  {
    "path": "example/README.md",
    "content": "# sample\n\nA new Flutter project.\n\n## Getting Started\n\nThis project is a starting point for a Flutter application.\n\nA few resources to get you started if this is your first Flutter project:\n\n- [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab)\n- [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook)\n\nFor help getting started with Flutter, view our \n[online documentation](https://flutter.io/docs), which offers tutorials, \nsamples, guidance on mobile development, and a full API reference.\n"
  },
  {
    "path": "example/lib/app.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport 'global_store/state.dart';\nimport 'global_store/store.dart';\nimport 'todo_edit_page/page.dart';\nimport 'todo_list_page/page.dart';\n\n/// 创建应用的根 Widget\n/// 1. 创建一个简单的路由，并注册页面\n/// 2. 对所需的页面进行和 AppStore 的连接\n/// 3. 对所需的页面进行 AOP 的增强\nWidget createApp() {\n  final AbstractRoutes routes = PageRoutes(\n    pages: <String, Page<Object, dynamic>>{\n      /// 注册TodoList主页面\n      'todo_list': ToDoListPage(),\n\n      /// 注册Todo编辑页面\n      'todo_edit': TodoEditPage(),\n    },\n    visitor: (String path, Page<Object, dynamic> page) {\n      /// 只有特定的范围的 Page 才需要建立和 AppStore 的连接关系\n      /// 满足 Page<T> ，T 是 GlobalBaseState 的子类\n      if (page.isTypeof<GlobalBaseState>()) {\n        /// 建立 AppStore 驱动 PageStore 的单向数据连接\n        /// 1. 参数1 AppStore\n        /// 2. 参数2 当 AppStore.state 变化时, PageStore.state 该如何变化\n        page.connectExtraStore<GlobalState>(GlobalStore.store,\n            (Object pagestate, GlobalState appState) {\n          final GlobalBaseState p = pagestate;\n          if (p.themeColor != appState.themeColor) {\n            if (pagestate is Cloneable) {\n              final Object copy = pagestate.clone();\n              final GlobalBaseState newState = copy;\n              newState.themeColor = appState.themeColor;\n              return newState;\n            }\n          }\n          return pagestate;\n        });\n      }\n\n      /// AOP\n      /// 页面可以有一些私有的 AOP 的增强， 但往往会有一些 AOP 是整个应用下，所有页面都会有的。\n      /// 这些公共的通用 AOP ，通过遍历路由页面的形式统一加入。\n      page.enhancer.append(\n        /// View AOP\n        viewMiddleware: <ViewMiddleware<dynamic>>[\n          safetyView<dynamic>(),\n        ],\n\n        /// Adapter AOP\n        adapterMiddleware: <AdapterMiddleware<dynamic>>[\n          safetyAdapter<dynamic>()\n        ],\n\n        /// Effect AOP\n        effectMiddleware: <EffectMiddleware<dynamic>>[\n          _pageAnalyticsMiddleware<dynamic>(),\n        ],\n\n        /// Store AOP\n        middleware: <Middleware<dynamic>>[\n          logMiddleware<dynamic>(tag: page.runtimeType.toString()),\n        ],\n      );\n    },\n  );\n\n  return MaterialApp(\n    title: 'Fluro',\n    debugShowCheckedModeBanner: false,\n    theme: ThemeData(\n      primarySwatch: Colors.blue,\n    ),\n    home: routes.buildPage('todo_list', null),\n    onGenerateRoute: (RouteSettings settings) {\n      return MaterialPageRoute<Object>(builder: (BuildContext context) {\n        return routes.buildPage(settings.name, settings.arguments);\n      });\n    },\n  );\n}\n\n/// 简单的 Effect AOP\n/// 只针对页面的生命周期进行打印\nEffectMiddleware<T> _pageAnalyticsMiddleware<T>({String tag = 'redux'}) {\n  return (AbstractLogic<dynamic> logic, Store<T> store) {\n    return (Effect<dynamic> effect) {\n      return (Action action, Context<dynamic> ctx) {\n        if (logic is Page<dynamic, dynamic> && action.type is Lifecycle) {\n          print('${logic.runtimeType} ${action.type.toString()} ');\n        }\n        return effect?.call(action, ctx);\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "example/lib/global_store/action.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nenum GlobalAction { changeThemeColor }\n\nclass GlobalActionCreator {\n  static Action onchangeThemeColor() {\n    return const Action(GlobalAction.changeThemeColor);\n  }\n}\n"
  },
  {
    "path": "example/lib/global_store/reducer.dart",
    "content": "import 'dart:ui';\n\nimport 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport 'action.dart';\nimport 'state.dart';\n\nReducer<GlobalState> buildReducer() {\n  return asReducer(\n    <Object, Reducer<GlobalState>>{\n      GlobalAction.changeThemeColor: _onchangeThemeColor,\n    },\n  );\n}\n\nList<Color> _colors = <Color>[\n  Colors.green,\n  Colors.red,\n  Colors.black,\n  Colors.blue\n];\n\nGlobalState _onchangeThemeColor(GlobalState state, Action action) {\n  final Color next =\n      _colors[((_colors.indexOf(state.themeColor) + 1) % _colors.length)];\n  return state.clone()..themeColor = next;\n}\n"
  },
  {
    "path": "example/lib/global_store/state.dart",
    "content": "import 'dart:ui';\n\nimport 'package:fish_redux/fish_redux.dart';\n\nabstract class GlobalBaseState {\n  Color get themeColor;\n  set themeColor(Color color);\n}\n\nclass GlobalState implements GlobalBaseState, Cloneable<GlobalState> {\n  @override\n  Color themeColor;\n\n  @override\n  GlobalState clone() {\n    return GlobalState();\n  }\n}\n"
  },
  {
    "path": "example/lib/global_store/store.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'reducer.dart';\nimport 'state.dart';\n\n/// 建立一个AppStore\n/// 目前它的功能只有切换主题\nclass GlobalStore {\n  static Store<GlobalState> _globalStore;\n\n  static Store<GlobalState> get store =>\n      _globalStore ??= createStore<GlobalState>(GlobalState(), buildReducer());\n}\n"
  },
  {
    "path": "example/lib/main.dart",
    "content": "import 'package:flutter/material.dart' hide Action, Page;\n\nimport 'app.dart';\n\nvoid main() => runApp(createApp());\n"
  },
  {
    "path": "example/lib/todo_edit_page/action.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nenum ToDoEditAction { onDone, onChangeTheme }\n\nclass ToDoEditActionCreator {\n  static Action onDone() {\n    return const Action(ToDoEditAction.onDone);\n  }\n\n  static Action onChangeTheme() {\n    return const Action(ToDoEditAction.onChangeTheme);\n  }\n}\n"
  },
  {
    "path": "example/lib/todo_edit_page/effect.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport '../global_store/action.dart';\nimport '../global_store/store.dart';\nimport '../todo_list_page/todo_component/component.dart';\nimport 'action.dart';\nimport 'state.dart';\n\nEffect<TodoEditState> buildEffect() {\n  return combineEffects(<Object, Effect<TodoEditState>>{\n    ToDoEditAction.onDone: _onDone,\n    ToDoEditAction.onChangeTheme: _onChangeTheme,\n  });\n}\n\nvoid _onDone(Action action, Context<TodoEditState> ctx) {\n  Navigator.of(ctx.context).pop<ToDoState>(\n    ctx.state.toDo.clone()\n      ..desc = ctx.state.descEditController.text\n      ..title = ctx.state.nameEditController.text,\n  );\n}\n\nvoid _onChangeTheme(Action action, Context<TodoEditState> ctx) {\n  GlobalStore.store.dispatch(GlobalActionCreator.onchangeThemeColor());\n}\n"
  },
  {
    "path": "example/lib/todo_edit_page/page.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nimport '../todo_list_page/todo_component/component.dart';\nimport 'effect.dart';\nimport 'state.dart';\nimport 'view.dart';\n\nclass TodoEditPage extends Page<TodoEditState, ToDoState> {\n  TodoEditPage()\n      : super(\n          initState: initState,\n          effect: buildEffect(),\n          view: buildView,\n\n          /// 页面私有AOP，如果需要\n          // middleware: <Middleware<TodoEditState>>[\n          //   logMiddleware(tag: 'TodoEditPage'),\n          // ],\n        );\n}\n"
  },
  {
    "path": "example/lib/todo_edit_page/state.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport '../global_store/state.dart';\nimport '../todo_list_page/todo_component/component.dart';\n\nclass TodoEditState implements GlobalBaseState, Cloneable<TodoEditState> {\n  ToDoState toDo;\n\n  TextEditingController nameEditController;\n  TextEditingController descEditController;\n\n  FocusNode focusNodeName;\n  FocusNode focusNodeDesc;\n\n  @override\n  Color themeColor;\n\n  @override\n  TodoEditState clone() {\n    return TodoEditState()\n      ..nameEditController = nameEditController\n      ..descEditController = descEditController\n      ..focusNodeName = focusNodeName\n      ..focusNodeDesc = focusNodeDesc\n      ..toDo = toDo\n      ..themeColor = themeColor;\n  }\n}\n\nTodoEditState initState(ToDoState arg) {\n  final TodoEditState state = TodoEditState();\n  state.toDo = arg?.clone() ?? ToDoState();\n  state.nameEditController = TextEditingController(text: arg?.title);\n  state.descEditController = TextEditingController(text: arg?.desc);\n  state.focusNodeName = FocusNode();\n  state.focusNodeDesc = FocusNode();\n\n  return state;\n}\n"
  },
  {
    "path": "example/lib/todo_edit_page/view.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport 'action.dart';\nimport 'state.dart';\n\nWidget buildView(\n    TodoEditState state, Dispatch dispatch, ViewService viewService) {\n  return Scaffold(\n    appBar: AppBar(\n      backgroundColor: state.themeColor,\n      title: const Text('Todo'),\n    ),\n    body: Container(\n      padding: const EdgeInsets.all(16.0),\n      child: Column(\n        children: <Widget>[\n          Container(\n            child: Row(\n              children: <Widget>[\n                Container(\n                  child: const Text('title:',\n                      style: TextStyle(color: Colors.black, fontSize: 20.0)),\n                  width: 56.0,\n                  alignment: AlignmentDirectional.topEnd,\n                ),\n                Expanded(\n                    child: Container(\n                  color: const Color(0xFFE0E0E0),\n                  padding: const EdgeInsets.all(8.0),\n                  margin: const EdgeInsets.only(left: 8.0),\n                  child: EditableText(\n                    controller: state.nameEditController,\n                    focusNode: state.focusNodeName,\n                    autofocus: true,\n                    style: const TextStyle(color: Colors.black, fontSize: 16.0),\n                    cursorColor: Colors.yellow,\n                    backgroundCursorColor: const Color(0xFFFFF59D),\n                  ),\n                ))\n              ],\n            ),\n          ),\n          RaisedButton(\n              padding: const EdgeInsets.only(\n                  left: 20.0, top: 10.0, right: 20.0, bottom: 10.0),\n              color: Colors.blue,\n              child: const Text('Change theme',\n                  style: TextStyle(fontSize: 18),\n                  overflow: TextOverflow.ellipsis),\n              onPressed: () {\n                dispatch(ToDoEditActionCreator.onChangeTheme());\n              }),\n          Expanded(\n              child: Container(\n            margin: const EdgeInsets.only(top: 32.0),\n            alignment: AlignmentDirectional.topStart,\n            child: Row(\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: <Widget>[\n                Container(\n                  child: const Text('desc:',\n                      style: TextStyle(color: Colors.black, fontSize: 20.0)),\n                  width: 56.0,\n                  alignment: AlignmentDirectional.topEnd,\n                ),\n                Expanded(\n                    child: Container(\n                  color: const Color(0xFFE0E0E0),\n                  padding: const EdgeInsets.all(8.0),\n                  margin: const EdgeInsets.only(left: 8.0),\n                  child: EditableText(\n                      controller: state.descEditController,\n                      backgroundCursorColor: const Color(0xFFE0E0E0),\n                      maxLines: 10,\n                      focusNode: state.focusNodeDesc,\n                      style:\n                          const TextStyle(color: Colors.black, fontSize: 16.0),\n                      cursorColor: Colors.yellow),\n                ))\n              ],\n            ),\n          ))\n        ],\n      ),\n    ),\n    floatingActionButton: FloatingActionButton(\n      onPressed: () => dispatch(ToDoEditActionCreator.onDone()),\n      tooltip: 'Done',\n      child: const Icon(Icons.done),\n    ),\n    floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,\n  );\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/action.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'todo_component/component.dart';\n\nenum PageAction { initToDos, onAdd }\n\nclass PageActionCreator {\n  static Action initToDosAction(List<ToDoState> toDos) {\n    return Action(PageAction.initToDos, payload: toDos);\n  }\n\n  static Action onAddAction() {\n    return const Action(PageAction.onAdd);\n  }\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/effect.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport 'action.dart';\nimport 'list_adapter/action.dart' as list_action;\nimport 'state.dart';\nimport 'todo_component/component.dart';\n\nEffect<PageState> buildEffect() {\n  return combineEffects(<Object, Effect<PageState>>{\n    Lifecycle.initState: _init,\n    PageAction.onAdd: _onAdd,\n  });\n}\n\nvoid _init(Action action, Context<PageState> ctx) {\n  final List<ToDoState> initToDos = <ToDoState>[\n    ToDoState(\n      uniqueId: '0',\n      title: 'Hello world',\n      desc: 'Learn how to program.',\n      isDone: true,\n    ),\n    ToDoState(\n      uniqueId: '1',\n      title: 'Hello Flutter',\n      desc: 'Learn how to build a flutter application.',\n      isDone: true,\n    ),\n    ToDoState(\n      uniqueId: '2',\n      title: 'How Fish Redux',\n      desc: 'Learn how to use Fish Redux in a flutter application.',\n      isDone: false,\n    )\n  ];\n\n  ctx.dispatch(PageActionCreator.initToDosAction(initToDos));\n}\n\nvoid _onAdd(Action action, Context<PageState> ctx) {\n  Navigator.of(ctx.context)\n      .pushNamed('todo_edit', arguments: null)\n      .then((dynamic toDo) {\n    if (toDo != null &&\n        (toDo.title?.isNotEmpty == true || toDo.desc?.isNotEmpty == true)) {\n      ctx.dispatch(list_action.ToDoListActionCreator.add(toDo));\n    }\n  });\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/flow_adapter/adapter.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nimport '../state.dart';\nimport '../todo_component/component.dart';\nimport 'reducer.dart';\nimport 'connector.dart';\n\nFlowAdapter<PageState> get adapter =>\n    FlowAdapter<PageState>(\n      reducer: buildReducer(),\n        view: (PageState state) =>\n            DependentArray<PageState>(\n              length: state.itemCount,\n              builder: (int index) {\n                return ToDoConnector(index: index) + ToDoComponent();\n              },\n            ));\n"
  },
  {
    "path": "example/lib/todo_list_page/flow_adapter/connector.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nimport '../state.dart';\nimport '../todo_component/component.dart';\n\nclass ToDoConnector\n    extends ConnOp<PageState, ToDoState> {\n\n      ToDoConnector({this.index});\n\n      final int index;\n\n  @override\n  ToDoState get(PageState state) {\n    if (index >= state.toDos.length) {\n      return null;\n    }\n    return state.toDos[index];\n  }\n\n  @override\n  void set(PageState state, ToDoState subState) {\n    state.toDos[index] = subState;\n  }\n}"
  },
  {
    "path": "example/lib/todo_list_page/flow_adapter/reducer.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nimport '../list_adapter/action.dart';\nimport '../state.dart';\nimport '../todo_component/action.dart' as todo_action;\nimport '../todo_component/component.dart';\n\nReducer<PageState> buildReducer() {\n  return asReducer(<Object, Reducer<PageState>>{\n    ToDoListAction.add: _add,\n    todo_action.ToDoAction.remove: _remove\n  });\n}\n\nPageState _add(PageState state, Action action) {\n  final ToDoState toDo = action.payload;\n  return state.clone()..toDos = (state.toDos.toList()..add(toDo));\n}\n\nPageState _remove(PageState state, Action action) {\n  final String unique = action.payload;\n  return state.clone()\n    ..toDos = (state.toDos.toList()\n      ..removeWhere((ToDoState state) => state.uniqueId == unique));\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/list_adapter/action.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport '../todo_component/component.dart';\n\nenum ToDoListAction { add }\n\nclass ToDoListActionCreator {\n  static Action add(ToDoState state) {\n    return Action(ToDoListAction.add, payload: state);\n  }\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/list_adapter/adapter.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nimport '../state.dart';\nimport '../todo_component/component.dart';\nimport 'reducer.dart';\n\nclass ToDoListAdapter extends SourceFlowAdapter<PageState> {\n  ToDoListAdapter()\n      : super(\n          pool: <String, Component<Object>>{\n            'toDo': ToDoComponent(),\n          },\n          reducer: buildReducer(),\n        );\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/list_adapter/reducer.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nimport '../state.dart';\nimport '../todo_component/action.dart' as todo_action;\nimport '../todo_component/component.dart';\nimport 'action.dart';\n\nReducer<PageState> buildReducer() {\n  return asReducer(<Object, Reducer<PageState>>{\n    ToDoListAction.add: _add,\n    todo_action.ToDoAction.remove: _remove\n  });\n}\n\nPageState _add(PageState state, Action action) {\n  final ToDoState toDo = action.payload;\n  return state.clone()..toDos = (state.toDos.toList()..add(toDo));\n}\n\nPageState _remove(PageState state, Action action) {\n  final String unique = action.payload;\n  return state.clone()\n    ..toDos = (state.toDos.toList()\n      ..removeWhere((ToDoState state) => state.uniqueId == unique));\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/page.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nimport 'effect.dart';\nimport 'list_adapter/adapter.dart';\nimport 'flow_adapter/adapter.dart';\nimport 'reducer.dart';\nimport 'report_component/component.dart';\nimport 'state.dart';\n\nimport 'view.dart';\n\nclass ToDoListPage extends Page<PageState, Map<String, dynamic>> {\n  ToDoListPage()\n      : super(\n          initState: initState,\n          effect: buildEffect(),\n          reducer: buildReducer(),\n          view: buildView,\n          dependencies: Dependencies<PageState>(\n              adapter: const NoneConn<PageState>() + adapter,//NoneConn<PageState>() + ToDoListAdapter(),\n              slots: <String, Dependent<PageState>>{\n                'report': ReportConnector() + ReportComponent()\n              }),\n\n          /// 页面私有AOP, 如果需要\n          // middleware: <Middleware<PageState>>[\n          //   logMiddleware(tag: 'ToDoListPage'),\n          // ],\n        );\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/reducer.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nimport 'action.dart';\nimport 'state.dart';\nimport 'todo_component/component.dart';\n\nReducer<PageState> buildReducer() {\n  return asReducer(\n    <Object, Reducer<PageState>>{PageAction.initToDos: _initToDosReducer},\n  );\n}\n\nPageState _initToDosReducer(PageState state, Action action) {\n  final List<ToDoState> toDos = action.payload ?? <ToDoState>[];\n  final PageState newState = state.clone();\n  newState.toDos = toDos;\n  return newState;\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/report_component/component.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nimport 'state.dart';\nimport 'view.dart';\n\nexport 'state.dart';\n\nclass ReportComponent extends Component<ReportState> {\n  ReportComponent()\n      : super(\n          view: buildView,\n        );\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/report_component/state.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nclass ReportState implements Cloneable<ReportState> {\n  int total;\n  int done;\n\n  ReportState({this.total = 0, this.done = 0});\n\n  @override\n  ReportState clone() {\n    return ReportState()\n      ..total = total\n      ..done = done;\n  }\n\n  @override\n  String toString() {\n    return 'ReportState{total: $total, done: $done}';\n  }\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/report_component/view.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport 'state.dart';\n\nWidget buildView(\n  ReportState state,\n  Dispatch dispatch,\n  ViewService viewService,\n) {\n  return Container(\n      margin: const EdgeInsets.all(8.0),\n      padding: const EdgeInsets.all(8.0),\n      color: Colors.blue,\n      child: Row(\n        children: <Widget>[\n          Container(\n            child: const Icon(Icons.report),\n            margin: const EdgeInsets.only(right: 8.0),\n          ),\n          Text(\n            'Total ${state.total} tasks, ${state.done} done.',\n            style: const TextStyle(fontSize: 18.0, color: Colors.white),\n          )\n        ],\n      ));\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/state.dart",
    "content": "import 'dart:ui';\n\nimport 'package:fish_redux/fish_redux.dart';\nimport '../global_store/state.dart';\nimport 'report_component/component.dart';\nimport 'todo_component/component.dart';\n\nclass PageState extends ItemListLike\n    implements GlobalBaseState, Cloneable<PageState> {\n  List<ToDoState> toDos;\n\n  @override\n  Color themeColor;\n\n  @override\n  PageState clone() {\n    return PageState()\n      ..toDos = toDos\n      ..themeColor = themeColor;\n  }\n\n  @override\n  Object getItemData(int index) => toDos[index];\n\n  @override\n  String getItemType(int index) => 'toDo';\n\n  @override\n  int get itemCount => toDos?.length ?? 0;\n\n  @override\n  ItemListLike updateItemData(int index, Object data, bool isStateCopied) {\n    toDos[index] = data;\n    return this;\n  }\n}\n\nPageState initState(Map<String, dynamic> args) {\n  //just demo, do nothing here...\n  return PageState();\n}\n\nclass ReportConnector extends ConnOp<PageState, ReportState>\n    with ReselectMixin<PageState, ReportState> {\n  @override\n  ReportState computed(PageState state) {\n    return ReportState()\n      ..done = state.toDos.where((ToDoState tds) => tds.isDone).length\n      ..total = state.toDos.length;\n  }\n\n  @override\n  List<dynamic> factors(PageState state) {\n    return <int>[\n      state.toDos.where((ToDoState tds) => tds.isDone).length,\n      state.toDos.length\n    ];\n  }\n\n  @override\n  void set(PageState state, ReportState subState) {\n    throw Exception('Unexcepted to set PageState from ReportState');\n  }\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/todo_component/action.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'state.dart';\n\nenum ToDoAction { onEdit, edit, done, onRemove, remove }\n\nclass ToDoActionCreator {\n  static Action onEditAction(String uniqueId) {\n    return Action(ToDoAction.onEdit, payload: uniqueId);\n  }\n\n  static Action editAction(ToDoState toDo) {\n    return Action(ToDoAction.edit, payload: toDo);\n  }\n\n  static Action doneAction(String uniqueId) {\n    return Action(ToDoAction.done, payload: uniqueId);\n  }\n\n  static Action onRemoveAction(String uniqueId) {\n    return Action(ToDoAction.onRemove, payload: uniqueId);\n  }\n\n  static Action removeAction(String uniqueId) {\n    return Action(ToDoAction.remove, payload: uniqueId);\n  }\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/todo_component/component.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nimport 'effect.dart';\nimport 'reducer.dart';\nimport 'state.dart';\nimport 'view.dart';\n\nexport 'state.dart';\n\nclass ToDoComponent extends Component<ToDoState> {\n  ToDoComponent()\n      : super(\n          view: buildView,\n          effect: buildEffect(),\n          reducer: buildReducer(),\n        );\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/todo_component/effect.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport 'action.dart';\nimport 'state.dart';\n\nEffect<ToDoState> buildEffect() {\n  return combineEffects(<Object, Effect<ToDoState>>{\n    ToDoAction.onEdit: _onEdit,\n    ToDoAction.onRemove: _onRemove,\n  });\n}\n\nvoid _onEdit(Action action, Context<ToDoState> ctx) {\n  if (action.payload == ctx.state.uniqueId) {\n    Navigator.of(ctx.context)\n        // .push<ToDoState>(MaterialPageRoute<ToDoState>(\n        //     builder: (BuildContext buildCtx) =>\n        //         edit_page.TodoEditPage().buildPage(ctx.state)))\n        .pushNamed('todo_edit', arguments: ctx.state)\n        .then((dynamic toDo) {\n      if (toDo != null) {\n        ctx.dispatch(ToDoActionCreator.editAction(toDo));\n      }\n    });\n  }\n}\n\nvoid _onRemove(Action action, Context<ToDoState> ctx) async {\n  final String select = await showDialog<String>(\n      context: ctx.context,\n      builder: (BuildContext buildContext) {\n        return AlertDialog(\n          title: Text('Are you sure to delete \"${ctx.state.title}\"?'),\n          actions: <Widget>[\n            GestureDetector(\n              child: const Text(\n                'Cancel',\n                style: TextStyle(fontSize: 16.0),\n              ),\n              onTap: () => Navigator.of(buildContext).pop(),\n            ),\n            GestureDetector(\n              child: const Text('Yes', style: TextStyle(fontSize: 16.0)),\n              onTap: () => Navigator.of(buildContext).pop('Yes'),\n            )\n          ],\n        );\n      });\n\n  if (select == 'Yes') {\n    ctx.dispatch(ToDoActionCreator.removeAction(ctx.state.uniqueId));\n  }\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/todo_component/reducer.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nimport 'action.dart';\nimport 'state.dart';\n\nReducer<ToDoState> buildReducer() {\n  return asReducer(<Object, Reducer<ToDoState>>{\n    ToDoAction.edit: _edit,\n    ToDoAction.done: _markDone\n  });\n}\n\nToDoState _edit(ToDoState state, Action action) {\n  final ToDoState toDo = action.payload;\n  if (state.uniqueId == toDo.uniqueId) {\n    return state.clone()\n      ..title = toDo.title\n      ..desc = toDo.desc;\n  }\n  return state;\n}\n\nToDoState _markDone(ToDoState state, Action action) {\n  final String uniqueId = action.payload;\n  if (state.uniqueId == uniqueId) {\n    return state.clone()..isDone = !state.isDone;\n  }\n  return state;\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/todo_component/state.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n// import 'package:uuid/uuid.dart';\n\nclass ToDoState implements Cloneable<ToDoState> {\n  String uniqueId;\n  String title;\n  String desc;\n  bool isDone;\n\n  static int _seed = 202103051044;\n\n  ToDoState({this.uniqueId, this.title, this.desc, this.isDone = false}) {\n    uniqueId ??= '${_seed++}';\n  }\n\n  @override\n  ToDoState clone() {\n    return ToDoState()\n      ..uniqueId = uniqueId\n      ..title = title\n      ..desc = desc\n      ..isDone = isDone;\n  }\n\n  @override\n  String toString() {\n    return 'ToDoState{uniqueId: $uniqueId, title: $title, desc: $desc, isDone: $isDone}';\n  }\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/todo_component/view.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport 'action.dart';\nimport 'state.dart';\n\nWidget buildView(\n  ToDoState state,\n  Dispatch dispatch,\n  ViewService viewService,\n) {\n  return Container(\n    padding: const EdgeInsets.all(8.0),\n    child: GestureDetector(\n      child: Column(\n        children: <Widget>[\n          GestureDetector(\n            child: Container(\n              height: 36.0,\n              color: state.isDone ? Colors.green : Colors.red,\n              child: Row(\n                children: <Widget>[\n                  Container(\n                    child: const Icon(Icons.label_outline),\n                    margin: const EdgeInsets.all(8.0),\n                  ),\n                  Expanded(\n                      child: Text(\n                    state.title ?? '',\n                    maxLines: 1,\n                    style: const TextStyle(color: Colors.white, fontSize: 18.0),\n                  )),\n                  GestureDetector(\n                    child: Container(\n                      margin: const EdgeInsets.only(right: 16.0),\n                      child: (() => state.isDone\n                          ? const Icon(Icons.check_box)\n                          : const Icon(Icons.check_box_outline_blank))(),\n                    ),\n                    onTap: () {\n                      dispatch(ToDoActionCreator.doneAction(state.uniqueId));\n                    },\n                  )\n                ],\n              ),\n              alignment: AlignmentDirectional.centerStart,\n            ),\n          ),\n          Container(\n            padding: const EdgeInsets.fromLTRB(16.0, 24.0, 16.0, 24.0),\n            color: const Color(0xFFE0E0E0),\n            child: Row(\n              children: <Widget>[\n                Expanded(\n                    child: Container(\n                  child: Text(\n                    state.desc ?? '',\n                    style: const TextStyle(color: Colors.black, fontSize: 16.0),\n                  ),\n                )),\n                GestureDetector(\n                  child: Container(\n                    child: const Icon(Icons.edit),\n                  ),\n                  onTap: () {\n                    dispatch(ToDoActionCreator.onEditAction(state.uniqueId));\n                  },\n                )\n              ],\n            ),\n          )\n        ],\n      ),\n      onLongPress: () {\n        dispatch(ToDoActionCreator.onRemoveAction(state.uniqueId));\n      },\n    ),\n  );\n}\n"
  },
  {
    "path": "example/lib/todo_list_page/view.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport 'action.dart';\nimport 'state.dart';\n\nWidget buildView(PageState state, Dispatch dispatch, ViewService viewService) {\n  final ListAdapter adapter = viewService.buildAdapter();\n  return Scaffold(\n    appBar: AppBar(\n      backgroundColor: state.themeColor,\n      title: const Text('ToDoList'),\n    ),\n    body: Container(\n      child: Column(\n        children: <Widget>[\n          viewService.buildComponent('report'),\n          Expanded(\n              child: ListView.builder(\n                  itemBuilder: adapter.itemBuilder,\n                  itemCount: adapter.itemCount))\n        ],\n      ),\n    ),\n    floatingActionButton: FloatingActionButton(\n      onPressed: () => dispatch(PageActionCreator.onAddAction()),\n      tooltip: 'Add',\n      child: const Icon(Icons.add),\n    ),\n  );\n}\n"
  },
  {
    "path": "example/pubspec.yaml",
    "content": "name: sample\ndescription: Demonstrates how to use the fish_redux.\n\n# The following defines the version and build number for your application.\n# A version number is three numbers separated by dots, like 1.2.43\n# followed by an optional build number separated by a +.\n# Both the version and the builder number may be overridden in flutter\n# build by specifying --build-name and --build-number, respectively.\n# Read more about versioning at semver.org.\nversion: 1.0.0+1\n\nenvironment:\n    sdk: '>=2.0.0-dev.68.0 <3.0.0'\n\ndependencies:\n    fish_redux:\n        path: ../\n    flutter:\n        sdk: flutter \n    path_provider: ^0.4.1 \n\ndev_dependencies:\n    test: ^1.3.0\n    mockito: ^3.0.0\n    flutter_driver:\n        sdk: flutter\n    flutter_test:\n        sdk: flutter\n\n# For information on the generic Dart part of this file, see the\n# following page: https://www.dartlang.org/tools/pub/pubspec\n\n# The following section is specific to Flutter.\nflutter:\n    # The following line ensures that the Material Icons font is\n    # included with your application, so that you can use the icons in\n    # the Icons class.\n    uses-material-design: true\n    # To add assets to your application, add an assets section, like this:\n    # assets:\n    #  - images/a_dot_burr.jpeg\n    #  - images/a_dot_ham.jpeg\n    # An image asset can refer to one or more resolution-specific \"variants\", see\n    # https://flutter.io/assets-and-images/#resolution-aware.\n    # For details regarding adding assets from package dependencies, see\n    # https://flutter.io/assets-and-images/#from-packages\n    # To add custom fonts to your application, add a fonts section here,\n    # in this \"flutter\" section. Each entry in this list should have a\n    # \"family\" key with the font family name, and a \"fonts\" key with a\n    # list giving the asset and other descriptors for the font. For\n    # example:\n    # fonts:\n    #   - family: Schyler\n    #     fonts:\n    #       - asset: fonts/Schyler-Regular.ttf\n    #       - asset: fonts/Schyler-Italic.ttf\n    #         style: italic\n    #   - family: Trajan Pro\n    #     fonts:\n    #       - asset: fonts/TrajanPro.ttf\n    #       - asset: fonts/TrajanPro_Bold.ttf\n    #         weight: 700\n    #\n    # For details regarding fonts from package dependencies,\n    # see https://flutter.io/custom-fonts/#from-packages\n"
  },
  {
    "path": "example/test/widget_test.dart",
    "content": "// This is a basic Flutter widget test.\n//\n// To perform an interaction with a widget in your test, use the WidgetTester\n// utility that Flutter provides. For example, you can send tap and scroll\n// gestures. You can also use WidgetTester to find child widgets in the widget\n// tree, read text, and verify that the values of widget properties are correct.\n\nimport 'package:flutter_test/flutter_test.dart';\n\nvoid main() {\n  testWidgets('Counter increments smoke test', (WidgetTester tester) async {});\n}\n"
  },
  {
    "path": "example/web/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!--\n    If you are serving your web app in a path other than the root, change the\n    href value below to reflect the base path you are serving from.\n\n    The path provided below has to start and end with a slash \"/\" in order for\n    it to work correctly.\n\n    Fore more details:\n    * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base\n  -->\n  <base href=\"/\">\n\n  <meta charset=\"UTF-8\">\n  <meta content=\"IE=Edge\" http-equiv=\"X-UA-Compatible\">\n  <meta name=\"description\" content=\"A new Flutter project.\">\n\n  <!-- iOS meta tags & icons -->\n  <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n  <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black\">\n  <meta name=\"apple-mobile-web-app-title\" content=\"example\">\n  <link rel=\"apple-touch-icon\" href=\"icons/Icon-192.png\">\n\n  <!-- Favicon -->\n  <link rel=\"icon\" type=\"image/png\" href=\"favicon.png\"/>\n\n  <title>example</title>\n  <link rel=\"manifest\" href=\"manifest.json\">\n</head>\n<body>\n  <!-- This script installs service_worker.js to provide PWA functionality to\n       application. For more information, see:\n       https://developers.google.com/web/fundamentals/primers/service-workers -->\n  <script>\n    if ('serviceWorker' in navigator) {\n      window.addEventListener('flutter-first-frame', function () {\n        navigator.serviceWorker.register('flutter_service_worker.js');\n      });\n    }\n  </script>\n  <script src=\"main.dart.js\" type=\"application/javascript\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "example/web/manifest.json",
    "content": "{\n    \"name\": \"example\",\n    \"short_name\": \"example\",\n    \"start_url\": \".\",\n    \"display\": \"standalone\",\n    \"background_color\": \"#0175C2\",\n    \"theme_color\": \"#0175C2\",\n    \"description\": \"A new Flutter project.\",\n    \"orientation\": \"portrait-primary\",\n    \"prefer_related_applications\": false,\n    \"icons\": [\n        {\n            \"src\": \"icons/Icon-192.png\",\n            \"sizes\": \"192x192\",\n            \"type\": \"image/png\"\n        },\n        {\n            \"src\": \"icons/Icon-512.png\",\n            \"sizes\": \"512x512\",\n            \"type\": \"image/png\"\n        }\n    ]\n}\n"
  },
  {
    "path": "fish_redux.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"NewModuleRootManager\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <sourceFolder url=\"file://$MODULE_DIR$/lib\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/sample\" isTestSource=\"true\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/.idea\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/cloud_firestore/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/cloud_firestore/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/cloud_firestore/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/cloud_firestore/example/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/cloud_firestore/example/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/cloud_firestore/example/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/firebase_auth/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/firebase_auth/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/firebase_auth/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/firebase_auth/example/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/firebase_auth/example/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/firebase_auth/example/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/firebase_core/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/firebase_core/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/firebase_core/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/firebase_core/example/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/firebase_core/example/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/firebase_core/example/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/path_provider/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/path_provider/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/path_provider/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/path_provider/example/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/path_provider/example/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/bloc_flutter/ios/.symlinks/plugins/path_provider/example/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/blocs/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/blocs/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/blocs/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/built_redux/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/built_redux/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/built_redux/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/firebase_flutter_repository/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/firebase_flutter_repository/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/firebase_flutter_repository/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/firebase_rtdb_flutter_repository/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/firebase_rtdb_flutter_repository/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/firebase_rtdb_flutter_repository/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/firestore_redux/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/firestore_redux/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/firestore_redux/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/fish_redux/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/fish_redux/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/fish_redux/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/fish_redux/ios/.symlinks/plugins/path_provider/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/fish_redux/ios/.symlinks/plugins/path_provider/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/fish_redux/ios/.symlinks/plugins/path_provider/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/fish_redux/ios/.symlinks/plugins/path_provider/example/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/fish_redux/ios/.symlinks/plugins/path_provider/example/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/fish_redux/ios/.symlinks/plugins/path_provider/example/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/inherited_widget/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/inherited_widget/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/inherited_widget/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/integration_tests/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/integration_tests/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/integration_tests/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvc/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvc/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvc/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvc/ios/.symlinks/plugins/path_provider/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvc/ios/.symlinks/plugins/path_provider/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvc/ios/.symlinks/plugins/path_provider/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvc/ios/.symlinks/plugins/path_provider/example/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvc/ios/.symlinks/plugins/path_provider/example/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvc/ios/.symlinks/plugins/path_provider/example/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvi_base/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvi_base/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvi_base/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvi_flutter/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvi_flutter/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvi_flutter/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvu/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvu/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/mvu/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/redurx/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/redurx/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/redurx/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/redux/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/redux/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/redux/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/redux/ios/.symlinks/plugins/path_provider/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/redux/ios/.symlinks/plugins/path_provider/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/redux/ios/.symlinks/plugins/path_provider/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/redux/ios/.symlinks/plugins/path_provider/example/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/redux/ios/.symlinks/plugins/path_provider/example/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/redux/ios/.symlinks/plugins/path_provider/example/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/scoped_model/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/scoped_model/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/scoped_model/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/simple_bloc_flutter/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/simple_bloc_flutter/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/simple_bloc_flutter/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/simple_blocs/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/simple_blocs/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/simple_blocs/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/todos_repository/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/todos_repository/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/todos_repository/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/todos_repository_flutter/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/todos_repository_flutter/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/todos_repository_flutter/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/vanilla/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/vanilla/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/flutter_architecture_samples/example/vanilla/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/sample/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/sample/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/sample/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/sample/integration_tests/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/sample/integration_tests/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/sample/integration_tests/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/sample/todos_repository_flutter/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/sample/todos_repository_flutter/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/sample/todos_repository_flutter/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/sample/todos_repository_flutter/todos_repository/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/sample/todos_repository_flutter/todos_repository/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/sample/todos_repository_flutter/todos_repository/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/test/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/test/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/test/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/test/test_widgets/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/test/test_widgets/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/test/test_widgets/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/test/widgets/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/test/widgets/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/test/widgets/build\" />\n    </content>\n    <orderEntry type=\"jdk\" jdkName=\"Android API 25 Platform\" jdkType=\"Android SDK\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n    <orderEntry type=\"library\" name=\"Dart SDK\" level=\"project\" />\n    <orderEntry type=\"library\" name=\"Flutter Plugins\" level=\"project\" />\n    <orderEntry type=\"library\" name=\"Dart Packages\" level=\"project\" />\n  </component>\n  <component name=\"sonarModuleSettings\">\n    <option name=\"localAnalysisScripName\" value=\"&lt;PROJECT&gt;\" />\n    <option name=\"serverName\" value=\"&lt;PROJECT&gt;\" />\n  </component>\n</module>"
  },
  {
    "path": "lib/fish_redux.dart",
    "content": "export 'src/extensions/extendsions.dart';\nexport 'src/redux/redux.dart';\nexport 'src/redux_adapter/redux_adapter.dart';\nexport 'src/redux_aop/redux_aop.dart';\nexport 'src/redux_component/redux_component.dart';\nexport 'src/redux_component_mixin/redux_component_mixin.dart';\nexport 'src/redux_connector/redux_connector.dart';\nexport 'src/redux_middleware/redux_middleware.dart';\nexport 'src/redux_routes/redux_routes.dart';\nexport 'src/utils/utils.dart';\n"
  },
  {
    "path": "lib/src/extensions/adapter_extensions.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/foundation.dart';\n\nimport '../redux/basic.dart';\nimport '../redux_adapter/redux_adapter.dart';\nimport '../redux_component/redux_component.dart';\nimport '../redux_connector/redux_connector.dart';\nimport 'connector_extensions.dart';\n\nclass SimpleFlowAdapter<T> extends FlowAdapter<T> {\n  SimpleFlowAdapter({\n    @required FlowAdapterView<T> view,\n    ReducerFilter<T> filter,\n    Reducer<T> reducer,\n    Effect<T> effect,\n    @deprecated Object Function(T state) key,\n  }) : super(\n          view: view,\n          filter: filter,\n          reducer: reducer,\n          effect: effect,\n          key: key,\n        );\n\n  SimpleFlowAdapter.static({\n    @required List<Dependent<T>> children,\n    ReducerFilter<T> filter,\n    Reducer<T> reducer,\n    Effect<T> effect,\n    @deprecated Object Function(T state) key,\n  }) : this(\n          view: _buildByStatic(children),\n          filter: filter,\n          reducer: reducer,\n          effect: effect,\n          key: key,\n        );\n\n  SimpleFlowAdapter.dynamic({\n    @required Map<String, AbstractLogic<Object>> pool,\n    @required AbstractConnector<T, List<ItemBean>> connector,\n    ReducerFilter<T> filter,\n    Reducer<T> reducer,\n    Effect<T> effect,\n    @deprecated Object Function(T state) key,\n  }) : this(\n          view: _buildByDynamic(pool: pool, connector: connector),\n          filter: filter,\n          reducer: reducer,\n          effect: effect,\n          key: key,\n        );\n\n  SimpleFlowAdapter.listLike({\n    @required Map<String, AbstractLogic<Object>> pool,\n    @required AbstractConnector<T, MutableItemListLike> connector,\n    ReducerFilter<T> filter,\n    Reducer<T> reducer,\n    Effect<T> effect,\n    @deprecated Object Function(T state) key,\n  }) : this(\n          view: _buildByListLike(pool: pool, connector: connector),\n          filter: filter,\n          reducer: reducer,\n          effect: effect,\n          key: key,\n        );\n}\n\nFlowAdapterView<T> _buildByStatic<T>(List<Dependent<T>> children) {\n  return (T state) {\n    return DependentArray<T>.fromList(children\n        .where((Dependent<T> dep) => dep.subGetter(() => state).call() != null)\n        .toList());\n  };\n}\n\nFlowAdapterView<T> _buildByListLike<T>({\n  @required Map<String, AbstractLogic<Object>> pool,\n  @required AbstractConnector<T, MutableItemListLike> connector,\n}) {\n  return (T state) {\n    final MutableItemListLike source = connector.get(state);\n    final DependentArray<T> depList = DependentArray<T>(\n      length: source.itemCount,\n      builder: (int index) {\n        final String type = source.getItemType(index);\n        final Dependent<T> dep = ConnHelper.join(\n          ConnHelper.to(\n            connector,\n            IndexedListLikeConn<MutableItemListLike>(index),\n          ),\n          pool[type],\n        );\n        return dep;\n      },\n    );\n    return depList;\n  };\n}\n\nFlowAdapterView<T> _buildByDynamic<T>({\n  @required Map<String, AbstractLogic<Object>> pool,\n  @required AbstractConnector<T, List<ItemBean>> connector,\n}) {\n  return (T state) {\n    final List<ItemBean> list = connector.get(state);\n    final DependentArray<T> depList = DependentArray<T>(\n      length: list.length,\n      builder: (int index) {\n        assert(index < list.length);\n        if (index < list.length) {\n          final ItemBean ib = list[index];\n          assert(ib != null);\n          return ConnHelper.join<T, Object>(\n            ConnHelper.to<T, List<ItemBean>, Object>(\n              connector,\n              IndexedListConn<ItemBean>(index),\n            ),\n            pool[ib.type],\n          );\n        }\n        return null;\n      },\n    );\n    return depList;\n  };\n}\n"
  },
  {
    "path": "lib/src/extensions/component_extensions.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/widgets.dart' hide Action;\n\nabstract class SimpleComponent<T> extends Component<T> {\n  SimpleComponent({\n    ReducerFilter<T> filter,\n    Dependencies<T> dependencies,\n    ShouldUpdate<T> shouldUpdate,\n    WidgetWrapper wrapper,\n    @deprecated Key Function(T) key,\n    bool clearOnDependenciesChanged = false,\n  }) : super(\n          view: null,\n          reducer: null,\n          effect: null,\n          filter: filter,\n          dependencies: dependencies,\n          shouldUpdate: shouldUpdate,\n          wrapper: wrapper,\n          key: key,\n          clearOnDependenciesChanged: clearOnDependenciesChanged,\n        );\n\n  @override\n  ViewBuilder<T> get protectedView => view;\n\n  @override\n  Reducer<T> get protectedReducer => reducer;\n\n  @override\n  Effect<T> get protectedEffect => effect;\n\n  Widget view(T state, Dispatch dispatch, ViewService viewService);\n\n  T reducer(T state, Action action) {\n    return state;\n  }\n\n  /// interrupted if not [false, null]\n  dynamic effect(Action action, Context<T> ctx) {\n    return true;\n  }\n}\n"
  },
  {
    "path": "lib/src/extensions/connector_extensions.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nimport '../redux/redux.dart';\nimport '../redux_component/basic.dart';\nimport '../redux_connector/redux_connector.dart';\n\nmixin IndexedConnMixin<T, P> on AbstractConnector<T, P> {\n  P _cached;\n\n  int get index;\n\n  P getByIndex(T state, int index);\n\n  @override\n  P get(T state) {\n    final P newState = getByIndex(state, index);\n    return checkNextState(newState);\n  }\n\n  /// fix get 存在状态不同步\n  P checkNextState(Object newState) {\n    final Object lastState = _cached;\n    final Object nextState =\n        ((newState is! P || newState.runtimeType != lastState.runtimeType)\n                ? false\n                : (newState is StateKey ? newState.key() : null) ==\n                    (lastState is StateKey ? lastState.key() : null))\n            ? newState\n            : lastState;\n    return _cached = nextState;\n  }\n}\n\nabstract class ImmutableIndexedConn<T, P> extends ImmutableConn<T, P>\n    with ConnOpMixin<T, P>, IndexedConnMixin<T, P> {\n  @override\n  final int index;\n  ImmutableIndexedConn(this.index);\n\n  T setByIndex(T state, P subState, int index);\n\n  @override\n  T set(T state, P subState) => setByIndex(state, subState, index);\n}\n\nabstract class MutableIndexedConn<T, P> extends MutableConn<T, P>\n    with ConnOpMixin<T, P>, IndexedConnMixin<T, P> {\n  @override\n  final int index;\n  MutableIndexedConn(this.index);\n\n  void setByIndex(T state, P subState, int index);\n\n  @override\n  void set(T state, P subState) => setByIndex(state, subState, index);\n}\n\n///////////////////////////////////////////////////////////////////////////////\nclass IndexedListConn<P> extends MutableIndexedConn<List<P>, P>\n    with ConnOpMixin<List<P>, P>, IndexedConnMixin<List<P>, P> {\n  IndexedListConn(int index) : super(index);\n\n  @override\n  P getByIndex(List<P> state, int index) {\n    final P newState = state[index];\n    return checkNextState(newState);\n  }\n\n  @override\n  void setByIndex(List<P> state, Object subState, int index) {\n    state[index] = subState;\n  }\n}\n\nclass IndexedListLikeConn<T extends MutableItemListLike>\n    extends MutableIndexedConn<T, Object> with ConnOpMixin<T, Object> {\n  IndexedListLikeConn(int index) : super(index);\n\n  @override\n  Object getByIndex(T state, int index) {\n    final Object newState = state.getItemData(index);\n    return checkNextState(newState);\n  }\n\n  @override\n  void setByIndex(T state, Object subState, int index) {\n    state.setItemData(index, subState);\n  }\n}\n"
  },
  {
    "path": "lib/src/extensions/extendsions.dart",
    "content": "export 'adapter_extensions.dart';\nexport 'component_extensions.dart';\nexport 'connector_extensions.dart';\n"
  },
  {
    "path": "lib/src/redux/apply_middleware.dart",
    "content": "import 'basic.dart';\n\n/// Accumulate a list of Middleware that enhances Dispatch to the Store.\n/// The wrapped direction of the Store.dispatch is from inside to outside.\nStoreEnhancer<T> applyMiddleware<T>(List<Middleware<T>> middleware) {\n  return middleware == null || middleware.isEmpty\n      ? null\n      : (StoreCreator<T> creator) => (T initState, Reducer<T> reducer) {\n            assert(middleware != null && middleware.isNotEmpty);\n\n            final Store<T> store = creator(initState, reducer);\n            final Dispatch initialValue = store.dispatch;\n            store.dispatch = (Action action) {\n              throw Exception(\n                  'Dispatching while constructing your middleware is not allowed. '\n                  'Other middleware would not be applied to this dispatch.');\n            };\n            store.dispatch = middleware\n                .map((Middleware<T> middleware) => middleware(\n                      dispatch: (Action action) => store.dispatch(action),\n                      getState: store.getState,\n                    ))\n                .fold(\n                  initialValue,\n                  (Dispatch previousValue,\n                          Dispatch Function(Dispatch) element) =>\n                      element(previousValue),\n                );\n\n            return store;\n          };\n}\n"
  },
  {
    "path": "lib/src/redux/basic.dart",
    "content": "import 'dart:async';\n\n/// This document describes the core concepts under the Redux system and their standard definitions.\n/// Mainly includes:\n/// 1. The concepts of ReduxJs community\n///    Action          ---- Definition of intention by plain object\n///    Reducer<T>      ---- How to modify the data by a pure function\n///    Dispatch        ---- Expression of intention\n///    Middleware<T>   ---- AOP\n///    Store<T>        ---- State management center\n/// 2. Additional abstractions beyond the basic concepts of the ReduxJs community.\n///    Connector<S, P> ---- The connection between big object <S> and small object <P>\n///    SubReducer<T>   ---- A function that modifies data of partial <T>\n///    The role of this layer of abstraction\n///    a. It is obvious that the implementation of combineReducers are decoupled with the grammatical features of JS\n///    b. The deeper is the contradiction between the centralization of Redux and the division of components can be solved.\n\n/// Action is a way of defining \"intention\".\n///     1. It emphasizes the clarity of an intention, not the implementation of the intent.\n///     2. Usually the implementation of the intent is done by Effect or Reducer.\n///     3. type: indicates the type of intent; payload: the original information loaded with the intent.\n///     4. Action definitions and standards, strictly follow the definition and standards of Action in the Redux community.\nclass Action {\n  const Action(this.type, {this.payload});\n  final Object type;\n  final dynamic payload;\n}\n\n/// Definition of the standard Reducer.\n/// If the Reducer needs to respond to the Action, it returns a new state, otherwise it returns the old state.\ntypedef Reducer<T> = T Function(T state, Action action);\n\n/// Definition of the standard Dispatch.\n/// Send an \"intention\".\ntypedef Dispatch = dynamic Function(Action action);\n\n/// Definition of a standard subscription function.\n/// input a subscriber and output an anti-subscription function.\ntypedef Subscribe = void Function() Function(void Function() callback);\n\n/// ReplaceReducer 的定义\ntypedef ReplaceReducer<T> = void Function(Reducer<T> reducer);\n\n/// Definition of the standard observable flow.\ntypedef Observable<T> = Stream<T> Function();\n\n/// Definition of synthesizable functions.\ntypedef Composable<T> = T Function(T next);\n\n/// Definition of the function type that returns type R.\ntypedef Get<R> = R Function();\n\n/// Definition of the standard Middleware.\ntypedef Middleware<T> = Composable<Dispatch> Function({\n  Dispatch dispatch,\n  Get<T> getState,\n});\n\n/// Definition of the standard Store.\nclass Store<T> {\n  Get<T> getState;\n  Dispatch dispatch;\n  Subscribe subscribe;\n  Observable<T> observable;\n  ReplaceReducer<T> replaceReducer;\n  Future<dynamic> Function() teardown;\n}\n\n/// Create a store definition\ntypedef StoreCreator<T> = Store<T> Function(\n  T preloadedState,\n  Reducer<T> reducer,\n);\n\n/// Definition of Enhanced creating a store\ntypedef StoreEnhancer<T> = StoreCreator<T> Function(StoreCreator<T> creator);\n\n/// Definition of SubReducer\n/// [isStateCopied] is Used to optimize execution performance.\n/// Ensure that a T will be cloned at most once during the entire process.\ntypedef SubReducer<T> = T Function(T state, Action action, bool isStateCopied);\n\n/// Definition of Connector which connects Reducer<S> with Reducer<P>.\n/// 1. How to get an instance of type P from an instance of type S.\n/// 2. How to synchronize changes of an instance of type P to an instance of type S.\n/// 3. How to clone a new S.\nabstract class AbstractConnector<S, P> {\n  P get(S state);\n\n  /// For mutable state, there are three abilities needed to be met.\n  ///     1. get: (S) => P\n  ///     2. set: (S, P) => void\n  ///     3. shallow copy: s.clone()\n  ///\n  /// For immutable state, there are two abilities needed to be met.\n  ///     1. get: (S) => P\n  ///     2. set: (S, P) => S\n  ///\n  /// See in [connector].\n  SubReducer<S> subReducer(Reducer<P> reducer);\n}\n"
  },
  {
    "path": "lib/src/redux/combine_reducers.dart",
    "content": "import 'basic.dart';\n\n/// Combine an iterable of SubReducer<T> into one Reducer<T>\nReducer<T> combineSubReducers<T>(Iterable<SubReducer<T>> subReducers) {\n  final List<SubReducer<T>> notNullReducers = subReducers\n      ?.where((SubReducer<T> e) => e != null)\n      ?.toList(growable: false);\n\n  if (notNullReducers == null || notNullReducers.isEmpty) {\n    return null;\n  }\n\n  if (notNullReducers.length == 1) {\n    final SubReducer<T> single = notNullReducers.single;\n    return (T state, Action action) => single(state, action, false);\n  }\n\n  return (T state, Action action) {\n    T copy = state;\n    bool hasChanged = false;\n    for (SubReducer<T> subReducer in notNullReducers) {\n      copy = subReducer(copy, action, hasChanged);\n      hasChanged = hasChanged || copy != state;\n    }\n    assert(copy != null);\n    return copy;\n  };\n}\n\n/// Combine an iterable of Reducer<T> into one Reducer<T>\nReducer<T> combineReducers<T>(Iterable<Reducer<T>> reducers) {\n  final List<Reducer<T>> notNullReducers =\n      reducers?.where((Reducer<T> r) => r != null)?.toList(growable: false);\n  if (notNullReducers == null || notNullReducers.isEmpty) {\n    return null;\n  }\n\n  if (notNullReducers.length == 1) {\n    return notNullReducers.single;\n  }\n\n  return (T state, Action action) {\n    T nextState = state;\n    for (Reducer<T> reducer in notNullReducers) {\n      nextState = reducer(nextState, action);\n    }\n    assert(nextState != null);\n    return nextState;\n  };\n}\n\n/// Convert a super Reducer<Sup> to a sub Reducer<Sub>\nReducer<Sub> castReducer<Sub extends Sup, Sup>(Reducer<Sup> sup) {\n  return sup == null\n      ? null\n      : (Sub state, Action action) {\n          final Sub result = sup(state, action);\n          return result;\n        };\n}\n"
  },
  {
    "path": "lib/src/redux/connector.dart",
    "content": "import 'basic.dart';\n\n/// Define a basic connector for immutable state.\n///     /// Example:\n///     class State {\n///       final SubState sub;\n///       final String name;\n///       const State({this.sub, this.name});\n///     }\n///\n///     class SubState {}\n///\n///     class Conn<State, SubState> extends ImmutableConn<State, SubState> {\n///       SubState get(State state) => state.sub;\n///       State set(State state, SubState sub) => State(sub: sub, name: state.name);\n///     }\nabstract class ImmutableConn<T, P> implements AbstractConnector<T, P> {\n  const ImmutableConn();\n\n  T set(T state, P subState);\n\n  @override\n  SubReducer<T> subReducer(Reducer<P> reducer) {\n    return reducer == null\n        ? null\n        : (T state, Action action, bool isStateCopied) {\n            final P props = get(state);\n            if (props == null) {\n              return state;\n            }\n            final P newProps = reducer(props, action);\n            final bool hasChanged = !identical(newProps, props);\n            if (hasChanged) {\n              final T result = set(state, newProps);\n              assert(result != null, 'Expected to return a non-null value.');\n              return result;\n            }\n            return state;\n          };\n  }\n}\n\n/// Definition of Cloneable\nabstract class Cloneable<T extends Cloneable<T>> {\n  T clone();\n}\n\n/// how to clone an object\ndynamic _clone<T>(T state) {\n  if (state is Cloneable) {\n    return state.clone();\n  } else if (state is List) {\n    return state.toList();\n  } else if (state is Map<String, dynamic>) {\n    return <String, dynamic>{}..addAll(state);\n  } else if (state == null) {\n    return null;\n  } else {\n    throw ArgumentError(\n        'Could not clone this state of type ${state.runtimeType}.');\n  }\n}\n\n/// Define a basic connector for mutable state.\n///     /// Example:\n///     class State implments Cloneable<State>{\n///       SubState sub;\n///       String name;\n///       State({this.sub, this.name});\n///\n///       State clone() => State(sub: sub, name: name);\n///     }\n///\n///     class SubState {}\n///\n///     class Conn<State, SubState> extends MutableConn<State, SubState> {\n///       SubState get(State state) => state.sub;\n///       void set(State state, SubState sub) => state.sub = sub;\n///     }\nabstract class MutableConn<T, P> implements AbstractConnector<T, P> {\n  const MutableConn();\n\n  void set(T state, P subState);\n\n  @override\n  SubReducer<T> subReducer(Reducer<P> reducer) {\n    return reducer == null\n        ? null\n        : (T state, Action action, bool isStateCopied) {\n            final P props = get(state);\n            if (props == null) {\n              return state;\n            }\n            final P newProps = reducer(props, action);\n            final bool hasChanged = newProps != props;\n            final T copy =\n                (hasChanged && !isStateCopied) ? _clone<T>(state) : state;\n            if (hasChanged) {\n              set(copy, newProps);\n            }\n            return copy;\n          };\n  }\n}\n"
  },
  {
    "path": "lib/src/redux/create_store.dart",
    "content": "import 'dart:async';\n\nimport 'basic.dart';\n\nReducer<T> _noop<T>() => (T state, Action action) => state;\n\ntypedef _VoidCallback = void Function();\n\nvoid _throwIfNot(bool condition, [String message]) {\n  if (!condition) {\n    throw ArgumentError(message);\n  }\n}\n\nStore<T> _createStore<T>(final T preloadedState, final Reducer<T> reducer) {\n  _throwIfNot(\n    preloadedState != null,\n    'Expected the preloadedState to be non-null value.',\n  );\n\n  final List<_VoidCallback> _listeners = <_VoidCallback>[];\n  final StreamController<T> _notifyController =\n      StreamController<T>.broadcast(sync: true);\n\n  T _state = preloadedState;\n  Reducer<T> _reducer = reducer ?? _noop<T>();\n  bool _isDispatching = false;\n  bool _isDisposed = false;\n\n  return Store<T>()\n    ..getState = (() => _state)\n    ..dispatch = (Action action) {\n      _throwIfNot(action != null, 'Expected the action to be non-null value.');\n      _throwIfNot(action.type != null,\n          'Expected the action.type to be non-null value.');\n      _throwIfNot(!_isDispatching, 'Reducers may not dispatch actions.');\n\n      if (_isDisposed) {\n        return;\n      }\n\n      try {\n        _isDispatching = true;\n        _state = _reducer(_state, action);\n      } finally {\n        _isDispatching = false;\n      }\n\n      final List<_VoidCallback> _notifyListeners = _listeners.toList(\n        growable: false,\n      );\n      for (_VoidCallback listener in _notifyListeners) {\n        listener();\n      }\n\n      _notifyController.add(_state);\n    }\n    ..replaceReducer = (Reducer<T> replaceReducer) {\n      _reducer = replaceReducer ?? _noop;\n    }\n    ..subscribe = (_VoidCallback listener) {\n      _throwIfNot(\n        listener != null,\n        'Expected the listener to be non-null value.',\n      );\n      _throwIfNot(\n        !_isDispatching,\n        'You may not call store.subscribe() while the reducer is executing.',\n      );\n\n      _listeners.add(listener);\n\n      return () {\n        _throwIfNot(\n          !_isDispatching,\n          'You may not unsubscribe from a store listener while the reducer is executing.',\n        );\n        _listeners.remove(listener);\n      };\n    }\n    ..observable = (() => _notifyController.stream)\n    ..teardown = () {\n      _isDisposed = true;\n      _listeners.clear();\n      return _notifyController.close();\n    };\n}\n\n/// create a store with enhancer\nStore<T> createStore<T>(T preloadedState, Reducer<T> reducer,\n        [StoreEnhancer<T> enhancer]) =>\n    enhancer != null\n        ? enhancer(_createStore)(preloadedState, reducer)\n        : _createStore(preloadedState, reducer);\n\nStoreEnhancer<T> composeStoreEnhancer<T>(List<StoreEnhancer<T>> enhancers) =>\n    enhancers == null || enhancers.isEmpty\n        ? null\n        : enhancers.reduce((StoreEnhancer<T> previous, StoreEnhancer<T> next) =>\n            (StoreCreator<T> creator) => next(previous(creator)));\n"
  },
  {
    "path": "lib/src/redux/redux.dart",
    "content": "export 'apply_middleware.dart';\nexport 'basic.dart';\nexport 'combine_reducers.dart';\nexport 'connector.dart';\nexport 'create_store.dart';\n"
  },
  {
    "path": "lib/src/redux_adapter/adapter.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux/redux.dart';\nimport '../redux_component/redux_component.dart';\n\n/// abstract for custom extends\nabstract class Adapter<T> extends Logic<T> implements AbstractAdapter<T> {\n  final AdapterBuilder<T> _adapter;\n\n  AdapterBuilder<T> get protectedAdapter => _adapter;\n\n  Adapter({\n    @required AdapterBuilder<T> adapter,\n    Reducer<T> reducer,\n    ReducerFilter<T> filter,\n    Effect<T> effect,\n    Dependencies<T> dependencies,\n    @deprecated Object Function(T) key,\n  })  : assert(adapter != null),\n        assert(dependencies?.adapter == null,\n            'Unexpected dependencies.list for Adapter.'),\n        _adapter = adapter,\n        super(\n          reducer: reducer,\n          filter: filter,\n          effect: effect,\n          dependencies: dependencies,\n          // ignore:deprecated_member_use_from_same_package\n          key: key,\n        );\n\n  @override\n  ListAdapter buildAdapter(ContextSys<T> ctx) =>\n      ctx.enhancer\n          ?.adapterEnhance(protectedAdapter, this, ctx.store)\n          ?.call(ctx.state, ctx.dispatch, ctx) ??\n      protectedAdapter?.call(ctx.state, ctx.dispatch, ctx);\n\n  @override\n  ContextSys<T> createContext(\n    Store<Object> store,\n    BuildContext buildContext,\n    Get<T> getState, {\n    @required Enhancer<Object> enhancer,\n    @required DispatchBus bus,\n  }) {\n    assert(bus != null && enhancer != null);\n    return AdapterContext<T>(\n      logic: this,\n      store: store,\n      buildContext: buildContext,\n      getState: getState,\n      bus: bus,\n      enhancer: enhancer,\n    );\n  }\n}\n\nclass AdapterContext<T> extends LogicContext<T> {\n  AdapterContext({\n    @required AbstractAdapter<T> logic,\n    @required Store<Object> store,\n    @required BuildContext buildContext,\n    @required Get<T> getState,\n    @required DispatchBus bus,\n    @required Enhancer<Object> enhancer,\n  })  : assert(bus != null && enhancer != null),\n        super(\n          logic: logic,\n          store: store,\n          buildContext: buildContext,\n          getState: getState,\n          bus: bus,\n          enhancer: enhancer,\n        );\n\n  @override\n  ListAdapter buildAdapter() {\n    final AbstractAdapter<T> curLogic = logic;\n    return curLogic.buildAdapter(this);\n  }\n}\n"
  },
  {
    "path": "lib/src/redux_adapter/dynamic_flow_adapter.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux/redux.dart';\nimport '../redux_component/redux_component.dart';\nimport '../utils/utils.dart';\nimport 'recycle_context.dart';\n\n/// template is a map, driven by array\n/// Use [SimpleFlowAdapter.dynamic] instead of [DynamicFlowAdapter]\n/// see in example\n@deprecated\nclass DynamicFlowAdapter<T> extends Logic<T> with RecycleContextMixin<T> {\n  final Map<String, AbstractLogic<Object>> pool;\n  final AbstractConnector<T, List<ItemBean>> connector;\n\n  DynamicFlowAdapter({\n    @required this.pool,\n    @required this.connector,\n    ReducerFilter<T> filter,\n    Reducer<T> reducer,\n    Effect<T> effect,\n\n    /// implement [StateKey] in T instead of using key in Logic.\n    /// class T implements StateKey {\n    ///   Object _key = UniqueKey();\n    ///   Object key() => _key;\n    /// }\n    @deprecated Object Function(T) key,\n  }) : super(\n          reducer: _dynamicReducer(reducer, pool, connector),\n          effect: effect,\n          filter: filter,\n          dependencies: null,\n          // ignore:deprecated_member_use_from_same_package\n          key: key,\n        );\n\n  @override\n  ListAdapter buildAdapter(ContextSys<T> ctx) {\n    final List<ItemBean> list = connector.get(ctx.state);\n    assert(list != null);\n\n    final RecycleContext<T> recycleCtx = ctx;\n    final List<ListAdapter> adapters = <ListAdapter>[];\n\n    recycleCtx.markAllUnused();\n\n    for (int index = 0; index < list.length; index++) {\n      final ItemBean itemBean = list[index];\n      final String type = itemBean.type;\n      final AbstractLogic<Object> result = pool[type];\n      assert(\n          result != null, 'Type of $type has not benn registered in the pool.');\n      if (result != null) {\n        if (result is AbstractAdapter<Object>) {\n          final ContextSys<Object> subCtx = recycleCtx.reuseOrCreate(\n            Tuple2<Type, Object>(\n              result.runtimeType,\n              result.key(itemBean.data),\n            ),\n            () => result.createContext(\n              recycleCtx.store,\n              recycleCtx.context,\n              _subGetter(() => connector.get(recycleCtx.state), index),\n              bus: recycleCtx.bus,\n              enhancer: recycleCtx.enhancer,\n            ),\n          );\n\n          /// hack to reduce adapter's rebuilding\n          adapters.add(memoizeListAdapter(result, subCtx));\n        } else if (result is AbstractComponent<Object>) {\n          adapters.add(ListAdapter((BuildContext buildContext, int _) {\n            return result.buildComponent(\n              recycleCtx.store,\n              _subGetter(() => connector.get(recycleCtx.state), index),\n              bus: recycleCtx.bus,\n              enhancer: recycleCtx.enhancer,\n            );\n          }, 1));\n        }\n      }\n    }\n    recycleCtx.cleanUnused();\n\n    return combineListAdapters(adapters);\n  }\n}\n\n/// Generate reducer for List<ItemBean> and combine them into one\nReducer<T> _dynamicReducer<T>(\n  Reducer<T> reducer,\n  Map<String, AbstractLogic<Object>> pool,\n  AbstractConnector<T, List<ItemBean>> connector,\n) {\n  final Reducer<List<ItemBean>> dyReducer =\n      (List<ItemBean> state, Action action) {\n    List<ItemBean> copy;\n    for (int i = 0; i < state.length; i++) {\n      final ItemBean itemBean = state[i];\n      final AbstractLogic<Object> result = pool[itemBean.type];\n      if (result != null) {\n        final Object newData = result.onReducer(itemBean.data, action);\n        if (newData != itemBean.data) {\n          copy ??= state.toList();\n          copy[i] = itemBean.clone(data: newData);\n        }\n      }\n    }\n    return copy ?? state;\n  };\n\n  return combineReducers(<Reducer<T>>[\n    reducer,\n    combineSubReducers(<SubReducer<T>>[connector.subReducer(dyReducer)]),\n  ]);\n}\n\n/// Define itemBean how to get state with connector\n///\n/// [_isSimilar] return true just use newState after reducer safely\n/// [_isSimilar] return false we should use cache state before reducer invoke.\n/// for reducer change state immediately but sub component will refresh on next\n/// frame. in this time the sub component will use cache state.\nGet<Object> _subGetter(Get<List<ItemBean>> getter, int index) {\n  final List<ItemBean> curState = getter();\n  ItemBean cacheItem = curState[index];\n\n  return () {\n    final List<ItemBean> newState = getter();\n\n    /// Either all sub-components use cache or not.\n    if (newState != null && newState.length > index) {\n      final ItemBean newItem = newState[index];\n      if (_couldReuse(cacheItem, newItem)) {\n        cacheItem = newItem;\n      }\n    }\n\n    return cacheItem.data;\n  };\n}\n\nbool _couldReuse(ItemBean beanA, ItemBean beanB) {\n  if (beanA.type != beanB.type) {\n    return false;\n  }\n\n  final Object dataA = beanA.data;\n  final Object dataB = beanB.data;\n  if (dataA.runtimeType != dataB.runtimeType) {\n    return false;\n  }\n\n  final Object keyA = dataA is StateKey ? dataA.key() : null;\n  final Object keyB = dataB is StateKey ? dataB.key() : null;\n  return keyA == keyB;\n}\n"
  },
  {
    "path": "lib/src/redux_adapter/flow_adapter.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/widgets.dart' hide Action, Page;\nimport '../redux/redux.dart';\nimport '../redux_component/redux_component.dart';\nimport 'recycle_context.dart';\n\nclass FlowAdapter<T> extends Logic<T>\n    with RecycleContextMixin<T>\n    implements AbstractAdapter<T> {\n  final FlowDependencies<T> _flowDependencies;\n  FlowAdapter({\n    @required FlowAdapterView<T> view,\n    ReducerFilter<T> filter,\n    Reducer<T> reducer,\n    Effect<T> effect,\n    @deprecated Object Function(T state) key,\n  })  : assert(view != null),\n        _flowDependencies =\n            FlowDependencies<T>(_memoize<T, DependentArray<T>>(view)),\n        super(\n          reducer: reducer,\n          effect: effect,\n          filter: filter,\n          dependencies: null,\n          // ignore: deprecated_member_use_from_same_package\n          key: key,\n        );\n\n  @override\n  Reducer<T> get protectedDependenciesReducer =>_flowDependencies.createReducer();\n\n  @override\n  ListAdapter buildAdapter(ContextSys<T> ctx) {\n    final T state = ctx.state;\n    final DependentArray<T> depArray = _flowDependencies.build(state);\n\n    final RecycleContext<T> recycleCtx = ctx;\n    final List<ListAdapter> adapters = <ListAdapter>[];\n\n    recycleCtx.markAllUnused();\n\n    final int count = depArray.length;\n    for (int index = 0; index < count; index++) {\n      final Dependent<T> dependent = depArray[index];\n\n      if (dependent == null) {\n        continue;\n      }\n\n      if (dependent.isAdapter()) {\n        /// use dependent's key\n        final ContextSys<Object> subCtx = recycleCtx.reuseOrCreate(\n          dependent.key(state),\n          () {\n            return dependent.createContext(\n              recycleCtx.store,\n              recycleCtx.context,\n              recycleCtx.getState,\n              bus: recycleCtx.bus,\n              enhancer: recycleCtx.enhancer,\n            );\n          },\n        );\n\n        adapters.add(dependent.buildAdapter(subCtx));\n      } else if (dependent.isComponent()) {\n        adapters.add(ListAdapter((BuildContext buildContext, int index) {\n          return dependent.buildComponent(\n            recycleCtx.store,\n            recycleCtx.getState,\n            bus: recycleCtx.bus,\n            enhancer: recycleCtx.enhancer,\n          );\n        }, 1));\n      }\n    }\n    recycleCtx.cleanUnused();\n\n    return combineListAdapters(adapters);\n  }\n}\n\n//////////////////////////////////////////\ntypedef IndexedDependentBuilder<T> = Dependent<T> Function(int);\n\nclass DependentArray<T> {\n  final IndexedDependentBuilder<T> builder;\n  final int length;\n\n  DependentArray({@required this.builder, @required this.length})\n      : assert(builder != null && length >= 0);\n\n  DependentArray.fromList(List<Dependent<T>> list)\n      : this(builder: (int index) => list[index], length: list.length);\n\n  Dependent<T> operator [](int index) => builder(index);\n}\n\ntypedef FlowAdapterView<T> = DependentArray<T> Function(T);\n\nclass FlowDependencies<T> {\n  final FlowAdapterView<T> build;\n\n  const FlowDependencies(this.build);\n\n  Reducer<T> createReducer() => (T state, Action action) {\n        T copy = state;\n        bool hasChanged = false;\n        final DependentArray<T> list = build(state);\n        if (list != null) {\n          for (int i = 0; i < list.length; i++) {\n            final Dependent<T> dep = list[i];\n            final SubReducer<T> subReducer = dep?.createSubReducer();\n            if (subReducer != null) {\n              copy = subReducer(copy, action, hasChanged);\n              hasChanged = hasChanged || copy != state;\n            }\n          }\n        }\n        return copy;\n      };\n}\n\n//////////////////////////////////////////\n/// Use [ItemListLike] instead of [List<ItemItemBean>]\nabstract class ItemListLike {\n  int get itemCount;\n\n  String getItemType(int index);\n\n  Object getItemData(int index);\n\n  ItemListLike updateItemData(int index, Object data, bool isStateCopied);\n}\n\nabstract class MutableItemListLike extends ItemListLike {\n  @mustCallSuper\n  @override\n  MutableItemListLike updateItemData(\n      int index, Object data, bool isStateCopied) {\n    final MutableItemListLike result = isStateCopied ? this : clone();\n    return result..setItemData(index, data);\n  }\n\n  void setItemData(int index, Object data);\n\n  MutableItemListLike clone();\n}\n\nabstract class ImmutableItemListLike extends ItemListLike {\n  @mustCallSuper\n  @override\n  ImmutableItemListLike updateItemData(\n          int index, Object data, bool isStateCopied) =>\n      setItemData(index, data);\n\n  ImmutableItemListLike setItemData(int index, Object data);\n\n  ImmutableItemListLike clone();\n}\n\n//////////////////////////////////////////\nclass ItemBean {\n  final String type;\n  final Object data;\n\n  const ItemBean(this.type, this.data);\n\n  ItemBean clone({String type, Object data}) =>\n      ItemBean(type ?? this.type, data ?? this.data);\n}\n\n/// Optimize flow-adapter-view performance\nR Function(P) _memoize<P, R>(R Function(P) functor) {\n  bool hasInvoked = false;\n  P cahcedKey;\n  R cachedValue;\n  return (P param) {\n    if (!hasInvoked) {\n      hasInvoked = true;\n      cahcedKey = param;\n      cachedValue = functor(param);\n    } else if (param != cahcedKey) {\n      cahcedKey = param;\n      cachedValue = functor(param);\n    }\n    return cachedValue;\n  };\n}\n"
  },
  {
    "path": "lib/src/redux_adapter/recycle_context.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux/redux.dart';\nimport '../redux_component/redux_component.dart';\nimport 'adapter.dart';\n\nclass RecycleContext<T> extends AdapterContext<T> {\n  final Map<Object, List<ContextSys<Object>>> _cachedMap =\n      <Object, List<ContextSys<Object>>>{};\n  final Map<Object, int> _usedIndexMap = <Object, int>{};\n\n  RecycleContext({\n    @required AbstractAdapter<T> logic,\n    @required @required Store<Object> store,\n    @required BuildContext buildContext,\n    @required Get<T> getState,\n    @required DispatchBus bus,\n    @required Enhancer<Object> enhancer,\n  }) : super(\n          logic: logic,\n          store: store,\n          buildContext: buildContext,\n          getState: getState,\n          bus: bus,\n          enhancer: enhancer,\n        );\n\n  @override\n  void onLifecycle(Action action) {\n    _cachedMap.forEach((Object key, List<ContextSys<Object>> list) {\n      for (ContextSys<Object> sub in list) {\n        sub.onLifecycle(action);\n      }\n    });\n\n    super.onLifecycle(action);\n  }\n\n  void markAllUnused() {\n    _usedIndexMap.clear();\n  }\n\n  ContextSys<Object> reuseOrCreate(Object key, Get<ContextSys<Object>> create) {\n    final int length = _usedIndexMap[key] = (_usedIndexMap[key] ?? 0) + 1;\n    final List<ContextSys<Object>> list =\n        _cachedMap[key] ??= <ContextSys<Object>>[];\n\n    if (length > list.length) {\n      _cachedMap[key].add(\n        create()\n          ..setParent(this)\n          ..onLifecycle(LifecycleCreator.initState()),\n      );\n    }\n\n    return list[length - 1];\n  }\n\n  void cleanUnused() {\n    _cachedMap.removeWhere((Object key, List<ContextSys<Object>> value) {\n      final int usedCount = _usedIndexMap[key] ?? 0;\n\n      for (int i = usedCount; i < value.length; i++) {\n        value[i].onLifecycle(LifecycleCreator.dispose());\n        value[i].dispose();\n      }\n      value.removeRange(usedCount, value.length);\n\n      return usedCount == 0;\n    });\n  }\n}\n\nmixin RecycleContextMixin<T> implements AbstractAdapter<T> {\n  @override\n  RecycleContext<T> createContext(\n    Store<Object> store,\n    BuildContext buildContext,\n    Get<Object> getState, {\n    @required DispatchBus bus,\n    @required Enhancer<Object> enhancer,\n  }) {\n    assert(bus != null && enhancer != null);\n\n    return RecycleContext<T>(\n      logic: this,\n      store: store,\n      buildContext: buildContext,\n      getState: asGetter<T>(getState),\n      bus: bus,\n      enhancer: enhancer,\n    );\n  }\n}\n\nListAdapter combineListAdapters(Iterable<ListAdapter> adapters) {\n  final List<ListAdapter> list = adapters\n      .where((ListAdapter e) => e != null && e.itemCount > 0)\n      .toList(growable: false);\n\n  if (list.every((ListAdapter e) => e.itemCount == 1)) {\n    /// The result is AbstractComponent\n    return ListAdapter(\n      (BuildContext buildContext, final int index) =>\n          list[index].itemBuilder(buildContext, 0),\n      list.length,\n    );\n  } else if (list.length == 1) {\n    return list.single;\n  }\n\n  final int maxItemCount = list.fold(0, (int count, ListAdapter adapter) {\n    return count + adapter.itemCount;\n  });\n\n  /// The result is AbstractAdapter\n  return ListAdapter(\n    (BuildContext buildContext, final int index) {\n      assert(index >= 0 && index < maxItemCount);\n      int yIndex = index;\n      int xIndex = 0;\n      while (xIndex < list.length && list[xIndex].itemCount <= yIndex) {\n        yIndex -= list[xIndex].itemCount;\n        xIndex++;\n      }\n      assert(xIndex < list.length);\n      return list[xIndex].itemBuilder(buildContext, yIndex);\n    },\n    maxItemCount,\n  );\n}\n\nListAdapter memoizeListAdapter(\n  AbstractAdapterBuilder<Object> result,\n  ContextSys<Object> subCtx,\n) {\n  final Object newState = subCtx.state;\n  if (subCtx.extra['@last-state'] != newState) {\n    subCtx.extra['@last-state'] = newState;\n    subCtx.extra['@last-adapter'] =\n        _memoizeListAdapter(result.buildAdapter(subCtx));\n  }\n\n  return subCtx.extra['@last-adapter'];\n}\n\nListAdapter _memoizeListAdapter(ListAdapter adapter) {\n  if (adapter.itemCount > 0) {\n    final List<Widget> memoized =\n        List<Widget>.filled(adapter.itemCount, null, growable: false);\n    return ListAdapter((BuildContext context, int index) {\n      return (memoized[index] ??= adapter.itemBuilder(context, index));\n    }, adapter.itemCount);\n  } else {\n    return adapter;\n  }\n}\n"
  },
  {
    "path": "lib/src/redux_adapter/redux_adapter.dart",
    "content": "export 'adapter.dart';\nexport 'dynamic_flow_adapter.dart';\nexport 'flow_adapter.dart';\nexport 'recycle_context.dart';\nexport 'source_flow_adapter.dart';\nexport 'static_flow_adapter.dart';\n"
  },
  {
    "path": "lib/src/redux_adapter/source_flow_adapter.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux/redux.dart';\nimport '../redux_component/redux_component.dart';\nimport '../utils/utils.dart';\nimport 'recycle_context.dart';\n\n/// template is a map, driven by array\n/// Use [FlowAdapter.source] instead of [SourceFlowAdapter]\n/// see in example\n/// template is a map, driven by source\n@deprecated\nclass SourceFlowAdapter<T extends ItemListLike> extends Logic<T>\n    with RecycleContextMixin<T> {\n  final Map<String, AbstractLogic<Object>> pool;\n\n  SourceFlowAdapter({\n    @required this.pool,\n    ReducerFilter<T> filter,\n    Reducer<T> reducer,\n    Effect<T> effect,\n\n    /// implement [StateKey] in T instead of using key in Logic.\n    /// class T implements StateKey {\n    ///   Object _key = UniqueKey();\n    ///   Object key() => _key;\n    /// }\n    @deprecated Object Function(T) key,\n  }) : super(\n          reducer: _dynamicReducer(reducer, pool),\n          effect: effect,\n          filter: filter,\n          dependencies: null,\n          // ignore:deprecated_member_use_from_same_package\n          key: key,\n        );\n\n  @override\n  ListAdapter buildAdapter(ContextSys<T> ctx) {\n    final ItemListLike adapterSource = ctx.state;\n    assert(adapterSource != null);\n\n    final RecycleContext<T> recycleCtx = ctx;\n    final List<ListAdapter> adapters = <ListAdapter>[];\n\n    recycleCtx.markAllUnused();\n\n    for (int index = 0; index < adapterSource.itemCount; index++) {\n      final String type = adapterSource.getItemType(index);\n      final AbstractLogic<Object> result = pool[type];\n\n      assert(\n          result != null, 'Type of $type has not benn registered in the pool.');\n      if (result != null) {\n        if (result is AbstractAdapter<Object>) {\n          final ContextSys<Object> subCtx = recycleCtx.reuseOrCreate(\n            Tuple2<Type, Object>(\n              result.runtimeType,\n              result.key(adapterSource.getItemData(index)),\n            ),\n            () => result.createContext(\n              recycleCtx.store,\n              recycleCtx.context,\n              _subGetter(() => recycleCtx.state, index),\n              bus: recycleCtx.bus,\n              enhancer: recycleCtx.enhancer,\n            ),\n          );\n\n          /// hack to reduce adapter's rebuilding\n          adapters.add(memoizeListAdapter(result, subCtx));\n        } else if (result is AbstractComponent<Object>) {\n          adapters.add(ListAdapter((BuildContext buildContext, int _) {\n            return result.buildComponent(\n              recycleCtx.store,\n              _subGetter(() => recycleCtx.state, index),\n              bus: recycleCtx.bus,\n              enhancer: recycleCtx.enhancer,\n            );\n          }, 1));\n        }\n      }\n    }\n    recycleCtx.cleanUnused();\n\n    return combineListAdapters(adapters);\n  }\n}\n\n/// Generate reducer for List<ItemBean> and combine them into one\nReducer<T> _dynamicReducer<T extends ItemListLike>(\n  Reducer<T> reducer,\n  Map<String, AbstractLogic<Object>> pool,\n) {\n  final Reducer<T> dyReducer = (ItemListLike state, Action action) {\n    ItemListLike copy;\n    for (int i = 0; i < state.itemCount; i++) {\n      final AbstractLogic<Object> result = pool[state.getItemType(i)];\n      if (result != null) {\n        final Object oldData = state.getItemData(i);\n        final Object newData = result.onReducer(oldData, action);\n        if (newData != oldData) {\n          copy = state.updateItemData(i, newData, copy != null);\n        }\n      }\n    }\n    return copy ?? state;\n  };\n\n  return combineReducers(<Reducer<T>>[reducer, dyReducer]);\n}\n\n/// Define itemBean how to get state with connector\n///\n/// [_isSimilar] return true just use newState after reducer safely\n/// [_isSimilar] return false we should use cache state before reducer invoke.\n/// for reducer change state immediately but sub component will refresh on next\n/// frame. in this time the sub component will use cache state.\nGet<Object> _subGetter(Get<ItemListLike> getter, int index) {\n  final ItemListLike curState = getter();\n  String type = curState.getItemType(index);\n  Object data = curState.getItemData(index);\n\n  return () {\n    final ItemListLike newState = getter();\n\n    /// Either all sub-components use cache or not.\n    if (newState != null && newState.itemCount > index) {\n      final String newType = newState.getItemType(index);\n      final Object newData = newState.getItemData(index);\n\n      if (_couldReuse(\n        typeA: type,\n        typeB: newType,\n        dataA: data,\n        dataB: newData,\n      )) {\n        type = newType;\n        data = newData;\n      }\n    }\n\n    return data;\n  };\n}\n\nbool _couldReuse({String typeA, String typeB, Object dataA, Object dataB}) {\n  return typeA != typeB\n      ? false\n      : dataA.runtimeType != dataB.runtimeType\n          ? false\n          : (dataA is StateKey ? dataA.key() : null) ==\n              (dataB is StateKey ? dataB.key() : null);\n}\n"
  },
  {
    "path": "lib/src/redux_adapter/static_flow_adapter.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux/redux.dart';\nimport '../redux_component/redux_component.dart';\nimport '../utils/utils.dart';\nimport 'recycle_context.dart';\n\n/// template is a map, driven by array\n/// Use [FlowAdapter.static] instead of [StaticFlowAdapter]\n/// see in example\n/// template is an array, driven by map like\n@deprecated\nclass StaticFlowAdapter<T> extends Logic<T>\n    with RecycleContextMixin<T>\n    implements AbstractAdapter<T> {\n  final List<Dependent<T>> _slots;\n\n  StaticFlowAdapter({\n    @required List<Dependent<T>> slots,\n    Reducer<T> reducer,\n    Effect<T> effect,\n    ReducerFilter<T> filter,\n\n    /// implement [StateKey] in T instead of using key in Logic.\n    /// class T implements StateKey {\n    ///   Object _key = UniqueKey();\n    ///   Object key() => _key;\n    /// }\n    @deprecated Object Function(T) key,\n  })  : assert(slots != null),\n        _slots = Collections.compact(slots),\n        super(\n          reducer: combineReducers(<Reducer<T>>[\n            reducer,\n            combineSubReducers(\n              slots.map(\n                (Dependent<T> dependent) => dependent?.createSubReducer(),\n              ),\n            )\n          ]),\n          effect: effect,\n          filter: filter,\n          dependencies: null,\n          // ignore:deprecated_member_use_from_same_package\n          key: key,\n        );\n\n  @override\n  ListAdapter buildAdapter(ContextSys<T> ctx) {\n    final RecycleContext<T> recycleCtx = ctx;\n    final List<ListAdapter> adapters = <ListAdapter>[];\n\n    recycleCtx.markAllUnused();\n    for (int i = 0; i < _slots.length; i++) {\n      final Dependent<T> dependent = _slots[i];\n      final Object subObject = dependent.subGetter(recycleCtx.getState)();\n      if (!dependent.isComponent()) {\n        /// precondition is subObject != null\n        if (subObject != null) {\n          /// use index of key\n          final ContextSys<Object> subCtx = recycleCtx.reuseOrCreate(i, () {\n            return dependent.createContext(\n              recycleCtx.store,\n              recycleCtx.context,\n              recycleCtx.getState,\n              bus: recycleCtx.bus,\n              enhancer: recycleCtx.enhancer,\n            );\n          });\n\n          /// hack to reduce adapter's rebuilding\n          adapters.add(memoizeListAdapter(dependent, subCtx));\n        }\n      } else if (subObject != null) {\n        adapters.add(ListAdapter((BuildContext buildContext, int index) {\n          return dependent.buildComponent(\n            recycleCtx.store,\n            recycleCtx.getState,\n            bus: recycleCtx.bus,\n            enhancer: recycleCtx.enhancer,\n          );\n        }, 1));\n      }\n    }\n    recycleCtx.cleanUnused();\n\n    return combineListAdapters(adapters);\n  }\n}\n"
  },
  {
    "path": "lib/src/redux_aop/aop.dart",
    "content": "typedef TypedApplyLike<R> = R Function(List<dynamic>, [Map<Symbol, dynamic>]);\n\n/// Unified abstraction of functions which used in [Function.apply]\ntypedef ApplyLike = dynamic Function(List<dynamic>, [Map<Symbol, dynamic>]);\n\n/// Unified abstraction of function AOP, input one function output another with some enhancement inside.\ntypedef ApplyLikeEnhancer = ApplyLike Function(ApplyLike functor);\n\nApplyLike _identity(ApplyLike f) => f;\n\nApplyLikeEnhancer _combine(ApplyLikeEnhancer e0, ApplyLikeEnhancer e1) =>\n    (ApplyLike f) => (e1 ?? _identity)((e0 ?? _identity)(f));\n\nconst ApplyLikeEnhancer ApplyLikeEnhancerIdentity = _identity;\n\n/// Implement AOP with Currying tec.\n/// [AOP]: https://en.wikipedia.org/wiki/Aspect-oriented_programming\n/// [Currying]: https://en.wikipedia.org/wiki/Currying\n/// Process\n/// 1. Input user [Function]\n/// 2. Cast to [ApplyLike]\n/// 3. Add some enhancement (by [ApplyLikeEnhancer])\n/// 4. Get new [ApplyLike]\n/// 5. Cast to [TypedApplyLike]\n/// 6. Cast to user [Function]\nclass AOP {\n  final ApplyLikeEnhancer _enhancer;\n\n  AOP(List<ApplyLikeEnhancer> enhances)\n      : _enhancer = enhances?.isNotEmpty == true\n            ? enhances.reduce(_combine)\n            : ApplyLikeEnhancerIdentity;\n\n  TypedApplyLike<R> enhance<R>(Function functor) {\n    /// cast functor to ApplyLike\n    final ApplyLike init = (List<dynamic> positionalArguments,\n            [Map<Symbol, dynamic> namedArguments]) =>\n        Function.apply(functor, positionalArguments, namedArguments);\n\n    /// enhance ApplyLike\n    final ApplyLike enhanced = _enhancer(init);\n\n    /// if not enhanced\n    if (init == enhanced) {\n      return null;\n    }\n\n    /// cast ApplyLike to TypedApplyLike<R>\n    return (List<dynamic> positionalArguments,\n        [Map<Symbol, dynamic> namedArguments]) {\n      final R result = enhanced(positionalArguments);\n      return result;\n    };\n  }\n\n  R Function() withZero<R>(R Function() f) {\n    final TypedApplyLike<R> enhanced = enhance<R>(f);\n    return enhanced != null ? () => enhanced(<dynamic>[]) : f;\n  }\n\n  R Function(P) withOne<R, P>(R Function(P) f) {\n    final TypedApplyLike<R> enhanced = enhance<R>(f);\n    return enhanced != null ? (P p) => enhanced(<dynamic>[p]) : f;\n  }\n\n  R Function(P0, P1) withTwo<R, P0, P1>(R Function(P0, P1) f) {\n    final R Function(List<dynamic>) enhanced = enhance<R>(f);\n    return enhanced != null ? (P0 p0, P1 p1) => enhanced(<dynamic>[p0, p1]) : f;\n  }\n\n  R Function(P0, P1, P2) withThree<R, P0, P1, P2>(R Function(P0, P1, P2) f) {\n    final TypedApplyLike<R> enhanced = enhance<R>(f);\n    return enhanced != null\n        ? (P0 p0, P1 p1, P2 p2) => enhanced(<dynamic>[p0, p1, p2])\n        : f;\n  }\n\n  R Function(P0, P1, P2, P3) withFour<R, P0, P1, P2, P3>(\n      R Function(P0, P1, P2, P3) f) {\n    final TypedApplyLike<R> enhanced = enhance<R>(f);\n    return enhanced != null\n        ? (P0 p0, P1 p1, P2 p2, P3 p3) => enhanced(<dynamic>[p0, p1, p2, p3])\n        : f;\n  }\n\n  R Function(P0, P1, P2, P3, P4) withFive<R, P0, P1, P2, P3, P4>(\n      R Function(P0, P1, P2, P3, P4) f) {\n    final TypedApplyLike<R> enhanced = enhance<R>(f);\n    return enhanced != null\n        ? (P0 p0, P1 p1, P2 p2, P3 p3, P4 p4) =>\n            enhanced(<dynamic>[p0, p1, p2, p3, p4])\n        : f;\n  }\n\n  R Function(P0, P1, P2, P3, P4, P5) withSix<R, P0, P1, P2, P3, P4, P5>(\n      R Function(P0, P1, P2, P3, P4, P5) f) {\n    final TypedApplyLike<R> enhanced = enhance<R>(f);\n    return enhanced != null\n        ? (P0 p0, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) =>\n            enhanced(<dynamic>[p0, p1, p2, p3, p4, p5])\n        : f;\n  }\n}\n"
  },
  {
    "path": "lib/src/redux_aop/common_aop/common_aop.dart",
    "content": "export 'debounce.dart';\nexport 'delay.dart';\nexport 'log.dart';\nexport 'memoize.dart';\nexport 'performance.dart';\nexport 'throttle.dart';\nexport 'wait_until.dart';\n"
  },
  {
    "path": "lib/src/redux_aop/common_aop/debounce.dart",
    "content": "import 'dart:async';\n\nimport '../aop.dart';\n\n/// debounce the stream, means the [millis] span functor call once and drop other event.\n/// it difference with [throttle].\nApplyLikeEnhancer debounce(int millis) {\n  return (dynamic Function(List<dynamic>) functor) {\n    int idGenerator = 0;\n    return (List<dynamic> positionalArguments,\n        [Map<Symbol, dynamic> namedArguments]) async {\n      final int newId = ++idGenerator;\n      await Future<void>.delayed(Duration(milliseconds: millis));\n      if (newId == idGenerator) {\n        return functor(positionalArguments);\n      }\n    };\n  };\n}\n"
  },
  {
    "path": "lib/src/redux_aop/common_aop/debug.dart",
    "content": "bool _debugFlag = false;\n\n/// Is app run a debug mode.\nbool isDebug() {\n  /// Assert statements have no effect in production code;\n  /// they’re for development only. Flutter enables asserts in debug mode.\n  assert(() {\n    _debugFlag = true;\n    return _debugFlag;\n  }());\n  return _debugFlag;\n}\n"
  },
  {
    "path": "lib/src/redux_aop/common_aop/delay.dart",
    "content": "import 'dart:async';\n\nimport '../aop.dart';\n\n/// functor will be call after [millis].\nApplyLikeEnhancer delay(int millis) {\n  return (dynamic Function(List<dynamic>) functor) {\n    return (List<dynamic> positionalArguments,\n        [Map<Symbol, dynamic> namedArguments]) async {\n      await Future<void>.delayed(Duration(milliseconds: millis));\n      return functor(positionalArguments);\n    };\n  };\n}\n"
  },
  {
    "path": "lib/src/redux_aop/common_aop/log.dart",
    "content": "import 'dart:async';\n\nimport '../aop.dart';\nimport 'debug.dart';\n\n/// AOP for functor log.\nApplyLikeEnhancer logAOP(String tag) {\n  return isDebug()\n      ? (dynamic Function(List<dynamic>) functor) {\n          return (List<dynamic> positionalArguments,\n              [Map<Symbol, dynamic> namedArguments]) {\n            print('$tag input: $positionalArguments');\n            final Object result = functor(positionalArguments);\n            if (result is Future) {\n              result.then((Object r) {\n                print('$tag output <Future>: $r');\n                return r;\n              });\n            } else {\n              print('$tag output: $result');\n            }\n            return result;\n          };\n        }\n      : ApplyLikeEnhancerIdentity;\n}\n"
  },
  {
    "path": "lib/src/redux_aop/common_aop/memoize.dart",
    "content": "import '../aop.dart';\n\nbool _listEquals<E>(List<E> list1, List<E> list2) {\n  if (identical(list1, list2)) {\n    return true;\n  }\n  if (list1 == null || list2 == null) {\n    return false;\n  }\n  final int length = list1.length;\n  if (length != list2.length) {\n    return false;\n  }\n  for (int i = 0; i < length; i++) {\n    if (list1[i] != list2[i]) {\n      return false;\n    }\n  }\n  return true;\n}\n\n/// memoize returns cached result of function call when inputs were not changed from previous invocation.\nApplyLikeEnhancer memoize() {\n  return (dynamic Function(List<dynamic>) functor) {\n    List<dynamic> memoizeArguments;\n    dynamic memoizeResult;\n    bool hasBeenCalled = false;\n\n    return (List<dynamic> positionalArguments,\n        [Map<Symbol, dynamic> namedArguments]) {\n      if (!hasBeenCalled ||\n          !_listEquals<dynamic>(positionalArguments, memoizeArguments)) {\n        memoizeResult = functor(positionalArguments);\n        memoizeArguments = positionalArguments;\n        hasBeenCalled = true;\n      }\n\n      return memoizeResult;\n    };\n  };\n}\n"
  },
  {
    "path": "lib/src/redux_aop/common_aop/performance.dart",
    "content": "import 'dart:async';\n\nimport '../aop.dart';\nimport 'debug.dart';\n\nint _microSecsSinceEpoch() => DateTime.now().microsecondsSinceEpoch;\n\n/// functor performance by time consuming.\nApplyLikeEnhancer performanceAOP(String tag) {\n  return isDebug()\n      ? (dynamic Function(List<dynamic>) functor) {\n          return (List<dynamic> positionalArguments,\n              [Map<Symbol, dynamic> namedArguments]) {\n            final int marked = DateTime.now().microsecondsSinceEpoch;\n            final Object result = functor(positionalArguments);\n            if (result is Future) {\n              result.then((Object r) {\n                print(\n                    '$tag performance <Future>: ${_microSecsSinceEpoch() - marked}');\n                return r;\n              });\n            } else {\n              print('$tag performance: ${_microSecsSinceEpoch() - marked}');\n            }\n            return result;\n          };\n        }\n      : ApplyLikeEnhancerIdentity;\n}\n"
  },
  {
    "path": "lib/src/redux_aop/common_aop/throttle.dart",
    "content": "import '../aop.dart';\n\nint _microSecsSinceEpoch() => DateTime.now().microsecondsSinceEpoch;\n\n/// throttle the stream, means every [millis] span functor call once.\n/// it difference with [debounce].\nApplyLikeEnhancer throttle(int millis) {\n  return (dynamic Function(List<dynamic>) functor) {\n    int last = 0;\n    return (List<dynamic> positionalArguments,\n        [Map<Symbol, dynamic> namedArguments]) {\n      final int now = _microSecsSinceEpoch();\n      final int elapsed = now - last;\n      if (elapsed >= millis) {\n        last = now;\n        return functor(positionalArguments);\n      }\n    };\n  };\n}\n"
  },
  {
    "path": "lib/src/redux_aop/common_aop/wait_until.dart",
    "content": "import 'dart:async';\nimport '../aop.dart';\n\n/// Wait the future return.\nApplyLikeEnhancer waitUntil() {\n  return (dynamic Function(List<dynamic>) functor) {\n    bool isLocked = false;\n    return (List<dynamic> positionalArguments,\n        [Map<Symbol, dynamic> namedArguments]) {\n      if (isLocked) {\n        return null;\n      } else {\n        final Object result = functor(positionalArguments);\n        if (result is Future) {\n          isLocked = true;\n          return result.whenComplete(() {\n            isLocked = false;\n          });\n        }\n        return result;\n      }\n    };\n  };\n}\n"
  },
  {
    "path": "lib/src/redux_aop/redux_aop.dart",
    "content": "export 'aop.dart';\nexport 'common_aop/common_aop.dart';\n"
  },
  {
    "path": "lib/src/redux_component/auto_dispose.dart",
    "content": "class _Fields {\n  bool isDisposed = false;\n  Set<AutoDispose> children;\n  AutoDispose parent;\n  void Function() onDisposed;\n}\n\n/// Ultra-lightweight lifecycle management system\n/// When an object's dispose is called\n///  1. Dispose all children\n///  2. Cut off the connection with parent\n///  3. The hook function of onDisposed is triggered\n///  4. Status marked as isDisposed = true\nclass AutoDispose {\n  final _Fields _fields = _Fields();\n\n  void visit(void Function(AutoDispose) visitor) =>\n      _fields.children?.forEach(visitor);\n\n  bool get isDisposed => _fields.isDisposed;\n\n  void dispose() {\n    /// dispose all children\n    if (_fields.children != null) {\n      final List<AutoDispose> copy = _fields.children.toList(growable: false);\n      for (AutoDispose child in copy) {\n        child.dispose();\n      }\n      _fields.children = null;\n    }\n\n    /// Cut off the connection with parent.\n    _fields.parent?._fields?.children?.remove(this);\n    _fields.parent = null;\n\n    /// The hook function of onDisposed is triggered.\n    _fields.onDisposed?.call();\n    _fields.onDisposed = null;\n\n    /// Status marked as isDisposed = true.\n    _fields.isDisposed = true;\n  }\n\n  void onDisposed(void Function() onDisposed) {\n    assert(_fields.onDisposed == null);\n    if (_fields.isDisposed) {\n      onDisposed?.call();\n    } else {\n      _fields.onDisposed = onDisposed;\n    }\n  }\n\n  void setParent(AutoDispose newParent) {\n    assert(newParent != this);\n\n    final AutoDispose oldParent = _fields.parent;\n    if (oldParent == newParent || isDisposed) {\n      return;\n    }\n\n    if (newParent != null && newParent.isDisposed) {\n      dispose();\n      return;\n    }\n\n    if (newParent != null) {\n      newParent._fields.children ??= <AutoDispose>{};\n      newParent._fields.children.add(this);\n    }\n    if (oldParent != null) {\n      oldParent._fields.children.remove(this);\n    }\n    _fields.parent = newParent;\n  }\n\n  AutoDispose registerOnDisposed(void Function() onDisposed) => AutoDispose()\n    ..setParent(this)\n    ..onDisposed(onDisposed);\n}\n"
  },
  {
    "path": "lib/src/redux_component/basic.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux/redux.dart';\nimport 'auto_dispose.dart';\n\n/// Component's view part\n/// 1.State is used to decide how to render\n/// 2.Dispatch is used to send actions\n/// 3.ViewService is used to build sub-components or adapter.\ntypedef ViewBuilder<T> = Widget Function(\n  T state,\n  Dispatch dispatch,\n  ViewService viewService,\n);\n\n/// Define a base ListAdapter which is used for ListView.builder.\n/// Many small listAdapters could be merged to a bigger one.\nclass ListAdapter {\n  final int itemCount;\n  final IndexedWidgetBuilder itemBuilder;\n  const ListAdapter(this.itemBuilder, this.itemCount);\n}\n\n/// Adapter's view part\n/// 1.State is used to decide how to render\n/// 2.Dispatch is used to send actions\n/// 3.ViewService is used to build sub-components or adapter.\ntypedef AdapterBuilder<T> = ListAdapter Function(\n  T state,\n  Dispatch dispatch,\n  ViewService viewService,\n);\n\n/// Data driven ui\n/// 1. How to render\n/// 2. When to update\nabstract class ViewUpdater<T> {\n  Widget buildWidget();\n  void didUpdateWidget();\n  void onNotify();\n  void forceUpdate();\n  void clearCache();\n}\n\n/// A little different with Dispatch (with if it is interrupted).\n/// bool for sync-functions, interrupted if true\n/// Future<void> for async-functions, should always be interrupted.\n// typedef OnAction = Dispatch;\n\n/// Predicate if a component should be updated when the store is changed.\ntypedef ShouldUpdate<T> = bool Function(T old, T now);\n\n/// Interrupt if not null not false\n/// bool for sync-functions, interrupted if true\n/// Future<void> for async-functions, should always be interrupted.\ntypedef Effect<T> = dynamic Function(Action action, Context<T> ctx);\n\n/// AOP on view\n/// usage\n/// ViewMiddleware<T> safetyView<T>(\n///     {Widget Function(dynamic, StackTrace,\n///             {AbstractComponent<dynamic> component, Store<T> store})\n///         onError}) {\n///   return (AbstractComponent<dynamic> component, Store<T> store) {\n///     return (ViewBuilder<dynamic> next) {\n///       return isDebug()\n///           ? next\n///           : (dynamic state, Dispatch dispatch, ViewService viewService) {\n///               try {\n///                 return next(state, dispatch, viewService);\n///               } catch (e, stackTrace) {\n///                 return onError?.call(\n///                       e,\n///                       stackTrace,\n///                       component: component,\n///                       store: store,\n///                     ) ??\n///                     Container(width: 0, height: 0);\n///               }\n///             };\n///     };\n///   };\n/// }\ntypedef ViewMiddleware<T> = Composable<ViewBuilder<dynamic>> Function(\n  AbstractComponent<dynamic>,\n  Store<T>,\n);\n\n/// AOP on adapter\ntypedef AdapterMiddleware<T> = Composable<AdapterBuilder<dynamic>> Function(\n  AbstractAdapter<dynamic>,\n  Store<T>,\n);\n\n/// AOP on effect\n/// usage\n/// EffectMiddleware<T> pageAnalyticsMiddleware<T>() {\n///   return (AbstractLogic<dynamic> logic, Store<T> store) {\n///     return (Effect<dynamic> effect) {\n///       return effect == null ? null : (Action action, Context<dynamic> ctx) {\n///         if (logic is Page<dynamic, dynamic>) {\n///           print('${logic.runtimeType} ${action.type.toString()} ${ctx.hashCode}');\n///         }\n///         return effect(action, ctx);\n///       };\n///     };\n///   };\n/// }\ntypedef EffectMiddleware<T> = Composable<Effect<dynamic>> Function(\n  AbstractLogic<dynamic>,\n  Store<T>,\n);\n\n/// AOP in page on store, view, adapter, effect...\nabstract class Enhancer<T> {\n  ViewBuilder<K> viewEnhance<K>(\n    ViewBuilder<K> view,\n    AbstractComponent<K> component,\n    Store<T> store,\n  );\n\n  AdapterBuilder<K> adapterEnhance<K>(\n    AdapterBuilder<K> adapterBuilder,\n    AbstractAdapter<K> logic,\n    Store<T> store,\n  );\n\n  Effect<K> effectEnhance<K>(\n    Effect<K> effect,\n    AbstractLogic<K> logic,\n    Store<T> store,\n  );\n\n  StoreCreator<T> storeEnhance(StoreCreator<T> creator);\n\n  void unshift({\n    List<Middleware<T>> middleware,\n    List<ViewMiddleware<T>> viewMiddleware,\n    List<EffectMiddleware<T>> effectMiddleware,\n    List<AdapterMiddleware<T>> adapterMiddleware,\n  });\n\n  void append({\n    List<Middleware<T>> middleware,\n    List<ViewMiddleware<T>> viewMiddleware,\n    List<EffectMiddleware<T>> effectMiddleware,\n    List<AdapterMiddleware<T>> adapterMiddleware,\n  });\n}\n\n/// AOP End\n\nabstract class ExtraData {\n  /// Get|Set extra data in context if needed.\n  Map<String, Object> get extra;\n}\n\n/// Seen in view-part or adapter-part\nabstract class ViewService implements ExtraData {\n  /// The way to build adapter which is configured in Dependencies.list\n  ListAdapter buildAdapter();\n\n  /// The way to build slot component which is configured in Dependencies.slots\n  Widget buildComponent(String name, {Widget defaultWidget});\n\n  /// Get BuildContext from the host-widget\n  BuildContext get context;\n\n  /// Broadcast action(the intent) in app (inter-pages)\n  void broadcast(Action action);\n\n  /// Broadcast in all component receivers;\n  /// Dispatch is enough. Use [Dispatch] instead of [broadcastEffect]\n  /// [Dispatch] = [SelfEffect] | ([broadcastEffect] & [store.dispatch])\n  @deprecated\n  void broadcastEffect(Action action, {bool excluded});\n}\n\n///  Seen in effect-part\nabstract class Context<T> extends AutoDispose implements ExtraData {\n  /// Get the latest state\n  T get state;\n\n  /// The way to send action, which will be consumed by self, or by broadcast-module and store.\n  dynamic dispatch(Action action);\n\n  /// Get BuildContext from the host-widget\n  BuildContext get context;\n\n  /// In general, we should not need this field.\n  /// When we have to use this field, it means that we have encountered difficulties.\n  /// This is a contradiction between presentation & logical separation, and Flutter's Widgets system.\n  ///\n  /// How to use ?\n  /// For example, we want to use SingleTickerProviderStateMixin\n  /// We should\n  /// 1. Define a new Component mixin SingleTickerProviderMixin\n  ///    class MyComponent<T> extends Component<T> with SingleTickerProviderMixin<T> {}\n  /// 2. Get the CustomStfState via context.stfState in Effect.\n  ///    /// Through BuildContext -> StatefulElement -> State\n  ///    final TickerProvider tickerProvider = context.stfState;\n  ///    AnimationController controller = AnimationController(vsync: tickerProvider);\n  ///    context.dispatch(ActionCreator.createController(controller));\n  State get stfState;\n\n  /// The way to build slot component which is configured in Dependencies.slots\n  /// such as custom mask or dialog\n  Widget buildComponent(String name);\n\n  /// Broadcast action in app (inter-stores)\n  void broadcast(Action action);\n\n  /// Broadcast in all component receivers;\n  void broadcastEffect(Action action, {bool excluded});\n\n  /// add observable\n  void Function() addObservable(Subscribe observable);\n\n  void forceUpdate();\n\n  /// listen on the changes of some parts of <T>.\n  void Function() listen({\n    bool Function(T, T) isChanged,\n    void Function() onChange,\n  });\n}\n\n/// Seen in framework-component\nabstract class ContextSys<T> extends Context<T> implements ViewService {\n  /// Response to lifecycle calls\n  void onLifecycle(Action action);\n\n  void bindForceUpdate(void Function() forceUpdate);\n\n  Store<dynamic> get store;\n\n  Enhancer<dynamic> get enhancer;\n\n  DispatchBus get bus;\n}\n\nabstract class AbstractAdapterBuilder<T> {\n  ListAdapter buildAdapter(ContextSys<T> ctx);\n}\n\n/// Representation of each dependency\nabstract class Dependent<T> implements AbstractAdapterBuilder<Object> {\n  Get<Object> subGetter(Get<T> getter);\n\n  SubReducer<T> createSubReducer();\n\n  Widget buildComponent(\n    Store<Object> store,\n    Get<T> getter, {\n    @required DispatchBus bus,\n    @required Enhancer<Object> enhancer,\n  });\n\n  ContextSys<Object> createContext(\n    Store<Object> store,\n    BuildContext buildContext,\n    Get<T> getState, {\n    @required DispatchBus bus,\n    @required Enhancer<Object> enhancer,\n  });\n\n  bool isComponent();\n\n  bool isAdapter();\n\n  Object key(T state);\n}\n\n/// Encapsulation of the logic part of the component\n/// The logic is divided into two parts, Reducer & SideEffect.\nabstract class AbstractLogic<T> {\n  /// To create a reducer<T>\n  Reducer<T> createReducer();\n\n  /// To solve Reducer<Object> is neither a subtype nor a supertype of Reducer<T> issue.\n  Object onReducer(Object state, Action action);\n\n  /// To create each instance's side-effect-action-handler\n  Dispatch createEffectDispatch(ContextSys<T> ctx, Enhancer<Object> enhancer);\n\n  /// To create each instance's side-effect-action-handler\n  Dispatch createNextDispatch(ContextSys<T> ctx, Enhancer<Object> enhancer);\n\n  /// To create each instance's dispatch\n  /// Dispatch is the most important api for users which is provided by framework\n  Dispatch createDispatch(\n    Dispatch effectDispatch,\n    Dispatch nextDispatch,\n    ContextSys<T> ctx,\n  );\n\n  /// To create each instance's context\n  ContextSys<T> createContext(\n    Store<Object> store,\n    BuildContext buildContext,\n    Get<T> getState, {\n    @required DispatchBus bus,\n    @required Enhancer<Object> enhancer,\n  });\n\n  /// To create each instance's key (for recycle) if needed\n  Object key(T state);\n\n  /// Find a dependent by name\n  Dependent<T> slot(String name);\n\n  /// Get a adapter-dependent\n  Dependent<T> adapterDep();\n\n  Type get propertyType;\n}\n\nabstract class AbstractComponent<T> implements AbstractLogic<T> {\n  /// How to build component instance\n  Widget buildComponent(\n    Store<Object> store,\n    Get<T> getter, {\n    @required DispatchBus bus,\n    @required Enhancer<Object> enhancer,\n  });\n}\n\nabstract class AbstractAdapter<T>\n    implements AbstractLogic<T>, AbstractAdapterBuilder<T> {}\n\n/// Because a main reducer will be very complicated with multiple level's state.\n/// When a reducer is slow to handle an action, maybe we should use ReducerFilter to improve the performance.\ntypedef ReducerFilter<T> = bool Function(T state, Action action);\n\n/// implement [StateKey] in T .\n/// class T implements StateKey {\n///   Object _key = UniqueKey();\n///   Object key() => _key;\n/// }\n/// see [https://github.com/alibaba/fish-redux/issues/461]\nabstract class StateKey {\n  Object key();\n}\n\n/// Define a DispatchBus\nabstract class DispatchBus {\n  void attach(DispatchBus parent);\n\n  void detach();\n\n  void dispatch(Action action, {Dispatch excluded});\n\n  void broadcast(Action action, {DispatchBus excluded});\n\n  void Function() registerReceiver(Dispatch dispatch);\n}\n"
  },
  {
    "path": "lib/src/redux_component/batch_store.dart",
    "content": "import 'dart:async';\n\nimport 'package:flutter/scheduler.dart';\nimport 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux/redux.dart';\n\n/// batch notify to subscribers.\nmixin _BatchNotify<T> on Store<T> {\n  final List<void Function()> _listeners = <void Function()>[];\n  bool _isBatching = false;\n  bool _isSetupBatch = false;\n  T _prevState;\n\n  void setupBatch() {\n    if (!_isSetupBatch) {\n      _isSetupBatch = true;\n      super.subscribe(_batch);\n\n      subscribe = (void Function() callback) {\n        assert(callback != null);\n        _listeners.add(callback);\n        return () {\n          _listeners.remove(callback);\n        };\n      };\n    }\n  }\n\n  bool isInSuitablePhase() {\n    return SchedulerBinding.instance != null &&\n        SchedulerBinding.instance.schedulerPhase !=\n            SchedulerPhase.persistentCallbacks &&\n        !(SchedulerBinding.instance.schedulerPhase == SchedulerPhase.idle &&\n            WidgetsBinding.instance.renderViewElement == null);\n  }\n\n  void _batch() {\n    if (!isInSuitablePhase()) {\n      if (!_isBatching) {\n        _isBatching = true;\n        SchedulerBinding.instance.addPostFrameCallback((Duration duration) {\n          if (_isBatching) {\n            _batch();\n          }\n        });\n      }\n    } else {\n      final T curState = getState();\n      if (!identical(_prevState, curState)) {\n        _prevState = curState;\n\n        final List<void Function()> notifyListeners = _listeners.toList(\n          growable: false,\n        );\n        for (void Function() listener in notifyListeners) {\n          listener();\n        }\n\n        _isBatching = false;\n      }\n    }\n  }\n}\n\nclass _BatchStore<T> extends Store<T> with _BatchNotify<T> {\n  _BatchStore(Store<T> store) : assert(store != null) {\n    getState = store.getState;\n    subscribe = store.subscribe;\n    replaceReducer = store.replaceReducer;\n    dispatch = store.dispatch;\n    observable = store.observable;\n    teardown = store.teardown;\n\n    setupBatch();\n  }\n}\n\nStore<T> createBatchStore<T>(\n  T preloadedState,\n  Reducer<T> reducer, {\n  StoreEnhancer<T> storeEnhancer,\n}) =>\n    _BatchStore<T>(\n      createStore(\n        preloadedState,\n        _appendUpdateStateReducer<T>(reducer),\n        storeEnhancer,\n      ),\n    );\n\n/// connect with app-store\n\nenum _UpdateState { Assign }\n\n// replace current state\nReducer<T> _appendUpdateStateReducer<T>(Reducer<T> reducer) =>\n    (T state, Action action) => action.type == _UpdateState.Assign\n        ? action.payload\n        : reducer == null ? state : reducer(state, action);\n\nStore<T> connectStores<T, K>(\n  Store<T> mainStore,\n  Store<K> extraStore,\n  T Function(T, K) update,\n) {\n  final void Function() subscriber = () {\n    final T prevT = mainStore.getState();\n    final T nextT = update(prevT, extraStore.getState());\n    if (nextT != null && !identical(prevT, nextT)) {\n      mainStore.dispatch(Action(_UpdateState.Assign, payload: nextT));\n    }\n  };\n\n  final void Function() unsubscribe = extraStore.subscribe(subscriber);\n\n  /// should triggle once\n  subscriber();\n\n  final Future<dynamic> Function() superMainTD = mainStore.teardown;\n  mainStore.teardown = () {\n    unsubscribe?.call();\n    return superMainTD();\n  };\n\n  return mainStore;\n}\n"
  },
  {
    "path": "lib/src/redux_component/component.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux/basic.dart';\nimport 'basic.dart';\nimport 'context.dart';\nimport 'dependencies.dart';\nimport 'helper.dart';\nimport 'lifecycle.dart';\nimport 'logic.dart';\n\n/// Wrapper ComponentWidget if needed like KeepAlive, RepaintBoundary etc.\ntypedef WidgetWrapper = Widget Function(Widget child);\n\n@immutable\nabstract class Component<T> extends Logic<T> implements AbstractComponent<T> {\n  final ViewBuilder<T> _view;\n  final ShouldUpdate<T> _shouldUpdate;\n  final WidgetWrapper _wrapper;\n  final bool _clearOnDependenciesChanged;\n\n  ViewBuilder<T> get protectedView => _view;\n  ShouldUpdate<T> get protectedShouldUpdate => _shouldUpdate;\n  WidgetWrapper get protectedWrapper => _wrapper;\n  bool get protectedClearOnDependenciesChanged => _clearOnDependenciesChanged;\n\n  Component({\n    @required ViewBuilder<T> view,\n    Reducer<T> reducer,\n    ReducerFilter<T> filter,\n    Effect<T> effect,\n    Dependencies<T> dependencies,\n    ShouldUpdate<T> shouldUpdate,\n    WidgetWrapper wrapper,\n\n    /// implement [StateKey] in T instead of using key in Logic.\n    /// class T implements StateKey {\n    ///   Object _key = UniqueKey();\n    ///   Object key() => _key;\n    /// }\n    @deprecated Key Function(T) key,\n    bool clearOnDependenciesChanged = false,\n  })  : _view = view,\n        _wrapper = wrapper ?? _wrapperByDefault,\n        _shouldUpdate = shouldUpdate ?? updateByDefault<T>(),\n        _clearOnDependenciesChanged = clearOnDependenciesChanged,\n        super(\n          reducer: reducer,\n          filter: filter,\n          effect: effect,\n          dependencies: dependencies,\n          // ignore:deprecated_member_use_from_same_package\n          key: key,\n        );\n\n  @override\n  Widget buildComponent(\n    Store<Object> store,\n    Get<Object> getter, {\n    @required DispatchBus bus,\n    @required Enhancer<Object> enhancer,\n  }) {\n    /// Check bus: DispatchBusDefault(); enhancer: EnhancerDefault<Object>();\n    assert(bus != null && enhancer != null);\n\n    return protectedWrapper(\n      ComponentWidget<T>(\n        component: this,\n        getter: asGetter<T>(getter),\n        store: store,\n        key: key(getter()),\n        bus: bus,\n        enhancer: enhancer,\n      ),\n    );\n  }\n\n  @override\n  ComponentContext<T> createContext(\n    Store<Object> store,\n    BuildContext buildContext,\n    Get<T> getState, {\n    @required void Function() markNeedsBuild,\n    @required DispatchBus bus,\n    @required Enhancer<Object> enhancer,\n  }) {\n    assert(bus != null && enhancer != null);\n    return ComponentContext<T>(\n      logic: this,\n      store: store,\n      buildContext: buildContext,\n      getState: getState,\n      view: enhancer.viewEnhance(protectedView, this, store),\n      shouldUpdate: protectedShouldUpdate,\n      name: name,\n      markNeedsBuild: markNeedsBuild,\n      sidecarCtx: adapterDep()?.createContext(\n        store,\n        buildContext,\n        getState,\n        bus: bus,\n        enhancer: enhancer,\n      ),\n      enhancer: enhancer,\n      bus: bus,\n    );\n  }\n\n  ComponentState<T> createState() => ComponentState<T>();\n\n  String get name => cache<String>('name', () => runtimeType.toString());\n\n  static ShouldUpdate<K> neverUpdate<K>() => (K _, K __) => false;\n\n  static ShouldUpdate<K> alwaysUpdate<K>() => (K _, K __) => true;\n\n  static ShouldUpdate<K> updateByDefault<K>() =>\n      (K _, K __) => !identical(_, __);\n\n  static Widget _wrapperByDefault(Widget child) => child;\n}\n\nclass ComponentWidget<T> extends StatefulWidget {\n  final Component<T> component;\n  final Store<Object> store;\n  final Get<T> getter;\n  final DispatchBus bus;\n  final Enhancer<Object> enhancer;\n\n  const ComponentWidget({\n    @required this.component,\n    @required this.store,\n    @required this.getter,\n    this.bus,\n    this.enhancer,\n    Key key,\n  })  : assert(component != null),\n        assert(store != null),\n        assert(getter != null),\n        super(key: key);\n\n  @override\n  ComponentState<T> createState() => component.createState();\n}\n\nclass ComponentState<T> extends State<ComponentWidget<T>> {\n  ComponentContext<T> _ctx;\n\n  ComponentContext<T> get ctx => _ctx;\n\n  @mustCallSuper\n  @override\n  Widget build(BuildContext context) => _ctx.buildWidget();\n\n  @override\n  @protected\n  @mustCallSuper\n  void reassemble() {\n    super.reassemble();\n    _ctx.clearCache();\n    _ctx.onLifecycle(LifecycleCreator.reassemble());\n  }\n\n  @mustCallSuper\n  @override\n  void initState() {\n    super.initState();\n\n    /// init context\n    _ctx = widget.component.createContext(\n      widget.store,\n      context,\n      () => widget.getter(),\n      markNeedsBuild: () {\n        if (mounted) {\n          setState(() {});\n        }\n      },\n      bus: widget.bus,\n      enhancer: widget.enhancer,\n    );\n\n    /// register store.subscribe\n    _ctx.registerOnDisposed(widget.store.subscribe(() => _ctx.onNotify()));\n\n    _ctx.onLifecycle(LifecycleCreator.initState());\n  }\n\n  @mustCallSuper\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n\n    if (widget.component.protectedClearOnDependenciesChanged != false) {\n      _ctx.clearCache();\n    }\n\n    _ctx.onLifecycle(LifecycleCreator.didChangeDependencies());\n  }\n\n  @mustCallSuper\n  @override\n  void deactivate() {\n    super.deactivate();\n    _ctx.onLifecycle(LifecycleCreator.deactivate());\n  }\n\n  @mustCallSuper\n  @override\n  void didUpdateWidget(ComponentWidget<T> oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    _ctx.didUpdateWidget();\n    _ctx.onLifecycle(LifecycleCreator.didUpdateWidget());\n  }\n\n  @mustCallSuper\n  void disposeCtx() {\n    if (!_ctx.isDisposed) {\n      _ctx\n        ..onLifecycle(LifecycleCreator.dispose())\n        ..dispose();\n    }\n  }\n\n  @mustCallSuper\n  @override\n  void dispose() {\n    disposeCtx();\n    super.dispose();\n  }\n}\n"
  },
  {
    "path": "lib/src/redux_component/context.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux/redux.dart';\nimport 'auto_dispose.dart';\nimport 'basic.dart';\nimport 'lifecycle.dart';\n\nmixin _ExtraMixin {\n  Map<String, Object> _extra;\n  Map<String, Object> get extra => _extra ??= <String, Object>{};\n}\n\n/// Default Context\nabstract class LogicContext<T> extends ContextSys<T> with _ExtraMixin {\n  final AbstractLogic<T> logic;\n\n  @override\n  final Store<Object> store;\n  @override\n  final DispatchBus bus;\n  @override\n  final Enhancer<Object> enhancer;\n\n  final Get<T> getState;\n\n  void Function() _forceUpdate;\n\n  BuildContext _buildContext;\n  Dispatch _dispatch;\n  Dispatch _effectDispatch;\n\n  LogicContext({\n    @required this.logic,\n    @required this.store,\n    @required BuildContext buildContext,\n    @required this.getState,\n\n    /// pageBus\n    @required this.bus,\n    @required this.enhancer,\n  })  : assert(logic != null),\n        assert(store != null),\n        assert(buildContext != null),\n        assert(getState != null),\n        assert(bus != null && enhancer != null),\n        _buildContext = buildContext {\n    ///\n    _effectDispatch = logic.createEffectDispatch(this, enhancer);\n\n    /// create Dispatch\n    _dispatch = logic.createDispatch(\n      _effectDispatch,\n      logic.createNextDispatch(\n        this,\n        enhancer,\n      ),\n      this,\n    );\n\n    /// Register inter-component broadcast\n    registerOnDisposed(bus.registerReceiver(_effectDispatch));\n  }\n\n  @override\n  void bindForceUpdate(void Function() forceUpdate) {\n    assert(_forceUpdate == null);\n    _forceUpdate = forceUpdate;\n  }\n\n  @override\n  BuildContext get context => _buildContext;\n\n  @override\n  T get state => getState();\n\n  @override\n  dynamic dispatch(Action action) => _dispatch(action);\n\n  @override\n  Widget buildComponent(String name, {Widget defaultWidget}) {\n    assert(name != null, 'The name must be NotNull for buildComponent.');\n    final Dependent<T> dependent = logic.slot(name);\n    final Widget result = dependent?.buildComponent(store, getState,\n        bus: bus, enhancer: enhancer);\n    assert(result != null || defaultWidget != null,\n        'Could not found component by name \"$name.\" You can set a default widget for buildComponent');\n    return result ?? (defaultWidget ?? Container());\n  }\n\n  @override\n  void onLifecycle(Action action) {\n    assert(_throwIfDisposed());\n    _dispatch(action);\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n    _buildContext = null;\n    _forceUpdate = null;\n  }\n\n  bool _throwIfDisposed() {\n    if (isDisposed) {\n      throw Exception(\n          'Ctx has been disposed which could not been used any more.');\n    }\n    return true;\n  }\n\n  @override\n  State<StatefulWidget> get stfState {\n    assert(_buildContext is StatefulElement);\n    if (_buildContext is StatefulElement) {\n      final StatefulElement stfElement = _buildContext;\n      return stfElement.state;\n    }\n    return null;\n  }\n\n  @override\n  void broadcastEffect(Action action, {bool excluded}) =>\n      bus.dispatch(action, excluded: excluded == true ? _effectDispatch : null);\n\n  @override\n  void broadcast(Action action) => bus.broadcast(action);\n\n  @override\n  void Function() addObservable(Subscribe observable) {\n    final void Function() unsubscribe = observable(() {\n      _forceUpdate?.call();\n    });\n    registerOnDisposed(unsubscribe);\n    return unsubscribe;\n  }\n\n  @override\n  void forceUpdate() => _forceUpdate?.call();\n\n  @override\n  void Function() listen({\n    bool Function(T, T) isChanged,\n    @required void Function() onChange,\n  }) {\n    assert(onChange != null);\n    T oldState;\n    final AutoDispose disposable = registerOnDisposed(\n      store.subscribe(\n        () => () {\n          final T newState = state;\n          final bool flag = isChanged == null\n              ? !identical(oldState, newState)\n              : isChanged(oldState, newState);\n          oldState = newState;\n          if (flag) {\n            onChange();\n          }\n        },\n      ),\n    );\n\n    return () => disposable?.dispose();\n  }\n}\n\nclass ComponentContext<T> extends LogicContext<T> implements ViewUpdater<T> {\n  final ViewBuilder<T> view;\n  final ShouldUpdate<T> shouldUpdate;\n  final String name;\n  final Function() markNeedsBuild;\n  final ContextSys<Object> sidecarCtx;\n\n  Widget _widgetCache;\n  T _latestState;\n\n  ComponentContext({\n    @required AbstractComponent<T> logic,\n    @required Store<Object> store,\n    @required BuildContext buildContext,\n    @required Get<T> getState,\n    @required this.view,\n    @required this.shouldUpdate,\n    @required this.name,\n    @required this.markNeedsBuild,\n    @required this.sidecarCtx,\n    @required DispatchBus bus,\n    @required Enhancer<Object> enhancer,\n  })  : assert(bus != null && enhancer != null),\n        super(\n          logic: logic,\n          store: store,\n          buildContext: buildContext,\n          getState: getState,\n          bus: bus,\n          enhancer: enhancer,\n        ) {\n    _latestState = state;\n\n    sidecarCtx?.setParent(this);\n  }\n\n  @override\n  void onLifecycle(Action action) {\n    super.onLifecycle(action);\n    sidecarCtx?.onLifecycle(action);\n  }\n\n  @override\n  ListAdapter buildAdapter() {\n    assert(sidecarCtx != null);\n    return logic.adapterDep()?.buildAdapter(sidecarCtx) ??\n        const ListAdapter(null, 0);\n  }\n\n  @override\n  Widget buildWidget() {\n    Widget result = _widgetCache;\n    if (result == null) {\n      result = _widgetCache = view(state, dispatch, this);\n\n      dispatch(LifecycleCreator.build(name));\n    }\n    return result;\n  }\n\n  @override\n  void didUpdateWidget() {\n    final T now = state;\n    if (shouldUpdate(_latestState, now)) {\n      _widgetCache = null;\n      _latestState = now;\n    }\n  }\n\n  @override\n  void onNotify() {\n    final T now = state;\n    if (shouldUpdate(_latestState, now)) {\n      _widgetCache = null;\n\n      markNeedsBuild();\n\n      _latestState = now;\n    }\n  }\n\n  @override\n  void clearCache() {\n    _widgetCache = null;\n  }\n\n  @override\n  void forceUpdate() {\n    _widgetCache = null;\n\n    try {\n      markNeedsBuild();\n    } catch (e) {\n      /// TODO\n      /// should try-catch in force mode which is called from outside\n    }\n  }\n}\n\nclass PureViewViewService implements ViewService {\n  final DispatchBus bus;\n\n  @override\n  final BuildContext context;\n\n  PureViewViewService(this.bus, this.context);\n\n  @override\n  void broadcast(Action action) => bus.broadcast(action);\n\n  @override\n  void broadcastEffect(Action action, {bool excluded}) => bus.dispatch(action);\n\n  @override\n  ListAdapter buildAdapter() => throw Exception(\n      'Unexpected call of \"buildAdapter\" in a PureViewComponent');\n\n  @override\n  Widget buildComponent(String name, {Widget defaultWidget}) => throw Exception(\n      'Unexpected call of \"buildComponent\" in a PureViewComponent');\n\n  @override\n  Map<String, Object> get extra =>\n      throw Exception('Unexpected call of \"extra\" in a PureViewComponent');\n}\n"
  },
  {
    "path": "lib/src/redux_component/dependencies.dart",
    "content": "import '../redux/redux.dart';\nimport 'basic.dart';\n\nclass Dependencies<T> {\n  final Map<String, Dependent<T>> slots;\n  final Dependent<T> adapter;\n\n  /// Use [adapter: NoneConn<T>() + Adapter<T>()] instead of [adapter: Adapter<T>()],\n  /// Which is better reusability and consistency.\n  Dependencies({\n    this.slots,\n    this.adapter,\n  }) : assert(adapter == null || adapter.isAdapter(),\n            'The dependent must contains adapter.');\n\n  Reducer<T> createReducer() {\n    final List<SubReducer<T>> subs = <SubReducer<T>>[];\n    if (slots != null && slots.isNotEmpty) {\n      subs.addAll(slots.entries.map<SubReducer<T>>(\n        (MapEntry<String, Dependent<T>> entry) =>\n            entry.value.createSubReducer(),\n      ));\n    }\n\n    if (adapter != null) {\n      subs.add(adapter.createSubReducer());\n    }\n\n    return combineReducers(<Reducer<T>>[combineSubReducers(subs)]);\n  }\n\n  Dependent<T> slot(String type) => slots[type];\n\n  Dependencies<T> trim() =>\n      adapter != null || slots?.isNotEmpty == true ? this : null;\n}\n"
  },
  {
    "path": "lib/src/redux_component/dependent.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux/redux.dart';\nimport 'basic.dart';\n\nclass _Dependent<T, P> implements Dependent<T> {\n  final AbstractConnector<T, P> connector;\n  final AbstractLogic<P> logic;\n  final SubReducer<T> subReducer;\n\n  _Dependent({\n    @required this.logic,\n    @required this.connector,\n  })  : assert(logic != null),\n        assert(connector != null),\n        subReducer = logic.createReducer != null\n            ? connector.subReducer(logic.createReducer())\n            : null;\n\n  @override\n  SubReducer<T> createSubReducer() => subReducer;\n\n  @override\n  Widget buildComponent(\n    Store<Object> store,\n    Get<T> getter, {\n    @required DispatchBus bus,\n    @required Enhancer<Object> enhancer,\n  }) {\n    assert(bus != null && enhancer != null);\n    assert(isComponent(), 'Unexpected type of ${logic.runtimeType}.');\n    final AbstractComponent<P> component = logic;\n    return component.buildComponent(\n      store,\n      () => connector.get(getter()),\n      bus: bus,\n      enhancer: enhancer,\n    );\n  }\n\n  @override\n  ListAdapter buildAdapter(covariant ContextSys<P> ctx) {\n    assert(isAdapter(), 'Unexpected type of ${logic.runtimeType}.');\n    final AbstractAdapter<P> adapter = logic;\n    return adapter.buildAdapter(ctx);\n  }\n\n  @override\n  Get<P> subGetter(Get<T> getter) => () => connector.get(getter());\n\n  @override\n  ContextSys<P> createContext(\n    Store<Object> store,\n    BuildContext buildContext,\n    Get<T> getState, {\n    @required DispatchBus bus,\n    @required Enhancer<Object> enhancer,\n  }) {\n    assert(bus != null && enhancer != null);\n    return logic.createContext(\n      store,\n      buildContext,\n      subGetter(getState),\n      bus: bus,\n      enhancer: enhancer,\n    );\n  }\n\n  @override\n  bool isComponent() => logic is AbstractComponent;\n\n  @override\n  bool isAdapter() => logic is AbstractAdapter;\n\n  @override\n  Object key(T state) {\n    return Tuple3<Type, Type, Object>(\n      logic.runtimeType,\n      connector.runtimeType,\n      logic.key(connector.get(state)),\n    );\n  }\n}\n\nDependent<K> createDependent<K, T>(\n        AbstractConnector<K, T> connector, AbstractLogic<T> logic) =>\n    logic != null ? _Dependent<K, T>(connector: connector, logic: logic) : null;\n"
  },
  {
    "path": "lib/src/redux_component/dispatch_bus.dart",
    "content": "import '../redux/redux.dart';\nimport 'basic.dart';\n\nclass DispatchBusDefault implements DispatchBus {\n  final List<Dispatch> _dispatchList = <Dispatch>[];\n  DispatchBus parent;\n  void Function() unregister;\n\n  DispatchBusDefault();\n\n  @override\n  void attach(DispatchBus parent) {\n    this.parent = parent;\n    unregister?.call();\n    unregister = parent?.registerReceiver(dispatch);\n  }\n\n  @override\n  void detach() {\n    unregister?.call();\n  }\n\n  @override\n  void dispatch(Action action, {Dispatch excluded}) {\n    final List<Dispatch> list = _dispatchList\n        .where((Dispatch dispatch) => dispatch != excluded)\n        .toList(growable: false);\n\n    for (Dispatch dispatch in list) {\n      dispatch(action);\n    }\n  }\n\n  @override\n  void broadcast(Action action, {DispatchBus excluded}) {\n    parent?.dispatch(action, excluded: excluded?.dispatch);\n  }\n\n  @override\n  void Function() registerReceiver(Dispatch dispatch) {\n    assert(!_dispatchList.contains(dispatch),\n        'Do not register a dispatch which is already existed');\n\n    if (dispatch != null) {\n      _dispatchList.add(dispatch);\n      return () {\n        _dispatchList.remove(dispatch);\n      };\n    } else {\n      return null;\n    }\n  }\n}\n"
  },
  {
    "path": "lib/src/redux_component/enhancer.dart",
    "content": "import 'package:fish_redux/src/redux_component/helper.dart';\n\nimport '../redux/redux.dart';\nimport 'basic.dart';\n\nclass EnhancerDefault<T> implements Enhancer<T> {\n  StoreEnhancer<T> _storeEnhancer;\n  ViewMiddleware<T> _viewEnhancer;\n  EffectMiddleware<T> _effectEnhancer;\n  AdapterMiddleware<T> _adapterEnhancer;\n\n  final List<Middleware<T>> _middleware = <Middleware<T>>[];\n  final List<ViewMiddleware<T>> _viewMiddleware = <ViewMiddleware<T>>[];\n  final List<EffectMiddleware<T>> _effectMiddleware = <EffectMiddleware<T>>[];\n  final List<AdapterMiddleware<T>> _adapterMiddleware =\n      <AdapterMiddleware<T>>[];\n\n  EnhancerDefault({\n    List<Middleware<T>> middleware,\n    List<ViewMiddleware<T>> viewMiddleware,\n    List<EffectMiddleware<T>> effectMiddleware,\n    List<AdapterMiddleware<T>> adapterMiddleware,\n  }) {\n    append(\n      middleware: middleware,\n      viewMiddleware: viewMiddleware,\n      effectMiddleware: effectMiddleware,\n      adapterMiddleware: adapterMiddleware,\n    );\n  }\n\n  @override\n  void unshift({\n    List<Middleware<T>> middleware,\n    List<ViewMiddleware<T>> viewMiddleware,\n    List<EffectMiddleware<T>> effectMiddleware,\n    List<AdapterMiddleware<T>> adapterMiddleware,\n  }) {\n    if (middleware != null) {\n      _middleware.insertAll(0, middleware);\n      _storeEnhancer = applyMiddleware<T>(_middleware);\n    }\n    if (viewMiddleware != null) {\n      _viewMiddleware.insertAll(0, viewMiddleware);\n      _viewEnhancer = mergeViewMiddleware<T>(_viewMiddleware);\n    }\n    if (effectMiddleware != null) {\n      _effectMiddleware.insertAll(0, effectMiddleware);\n      _effectEnhancer = mergeEffectMiddleware<T>(_effectMiddleware);\n    }\n    if (adapterMiddleware != null) {\n      _adapterMiddleware.insertAll(0, adapterMiddleware);\n      _adapterEnhancer = mergeAdapterMiddleware<T>(_adapterMiddleware);\n    }\n  }\n\n  @override\n  void append({\n    List<Middleware<T>> middleware,\n    List<ViewMiddleware<T>> viewMiddleware,\n    List<EffectMiddleware<T>> effectMiddleware,\n    List<AdapterMiddleware<T>> adapterMiddleware,\n  }) {\n    if (middleware != null) {\n      _middleware.addAll(middleware);\n      _storeEnhancer = applyMiddleware<T>(_middleware);\n    }\n    if (viewMiddleware != null) {\n      _viewMiddleware.addAll(viewMiddleware);\n      _viewEnhancer = mergeViewMiddleware<T>(_viewMiddleware);\n    }\n    if (effectMiddleware != null) {\n      _effectMiddleware.addAll(effectMiddleware);\n      _effectEnhancer = mergeEffectMiddleware<T>(_effectMiddleware);\n    }\n    if (adapterMiddleware != null) {\n      _adapterMiddleware.addAll(adapterMiddleware);\n      _adapterEnhancer = mergeAdapterMiddleware<T>(_adapterMiddleware);\n    }\n  }\n\n  @override\n  ViewBuilder<K> viewEnhance<K>(\n    ViewBuilder<K> view,\n    AbstractComponent<K> component,\n    Store<T> store,\n  ) =>\n      _viewEnhancer?.call(component, store)?.call(_inverterView<K>(view)) ??\n      view;\n\n  @override\n  AdapterBuilder<K> adapterEnhance<K>(\n    AdapterBuilder<K> adapterBuilder,\n    AbstractAdapter<K> logic,\n    Store<T> store,\n  ) =>\n      _adapterEnhancer\n          ?.call(logic, store)\n          ?.call(_inverterAdapter<K>(adapterBuilder)) ??\n      adapterBuilder;\n\n  @override\n  Effect<K> effectEnhance<K>(\n    Effect<K> effect,\n    AbstractLogic<K> logic,\n    Store<T> store,\n  ) =>\n      _effectEnhancer?.call(logic, store)?.call(_inverterEffect<K>(effect)) ??\n      effect;\n\n  @override\n  StoreCreator<T> storeEnhance(StoreCreator<T> creator) =>\n      _storeEnhancer?.call(creator) ?? creator;\n\n  Effect<dynamic> _inverterEffect<K>(Effect<K> effect) => effect == null\n      ? null\n      : (Action action, Context<dynamic> ctx) => effect(action, ctx);\n\n  ViewBuilder<dynamic> _inverterView<K>(ViewBuilder<K> view) => view == null\n      ? null\n      : (dynamic state, Dispatch dispatch, ViewService viewService) =>\n          view(state, dispatch, viewService);\n\n  AdapterBuilder<dynamic> _inverterAdapter<K>(AdapterBuilder<K> adapter) =>\n      adapter == null\n          ? null\n          : (dynamic state, Dispatch dispatch, ViewService viewService) =>\n              adapter(state, dispatch, viewService);\n}\n"
  },
  {
    "path": "lib/src/redux_component/helper.dart",
    "content": "import 'dart:async';\n\nimport 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux/basic.dart';\nimport '../utils/utils.dart';\nimport 'basic.dart';\nimport 'lifecycle.dart';\n\nGet<T> asGetter<T>(Get<Object> getter) {\n  Get<T> runtimeGetter;\n  if (getter is Get<T>) {\n    runtimeGetter = getter;\n  } else {\n    runtimeGetter = () {\n      final T result = getter();\n      return result;\n    };\n  }\n  return runtimeGetter;\n}\n\nAdapterBuilder<T> asAdapter<T>(ViewBuilder<T> view) {\n  return (T unstableState, Dispatch dispatch, ViewService service) {\n    final ContextSys<T> ctx = service;\n    return ListAdapter(\n      (BuildContext buildContext, int index) =>\n          view(ctx.state, dispatch, service),\n      1,\n    );\n  };\n}\n\nReducer<T> mergeReducers<T extends K, K>(Reducer<K> sup, [Reducer<T> sub]) {\n  return (T state, Action action) {\n    return sub?.call(sup(state, action), action) ?? sup(state, action);\n  };\n}\n\nEffect<T> mergeEffects<T extends K, K>(Effect<K> sup, [Effect<T> sub]) {\n  return (Action action, Context<T> ctx) {\n    return sub?.call(action, ctx) ?? sup.call(action, ctx);\n  };\n}\n\n/// combine & as\n/// for action.type which override it's == operator\nReducer<T> asReducer<T>(Map<Object, Reducer<T>> map) => (map == null ||\n        map.isEmpty)\n    ? null\n    : (T state, Action action) =>\n        map.entries\n            .firstWhere(\n                (MapEntry<Object, Reducer<T>> entry) =>\n                    action.type == entry.key,\n                orElse: () => null)\n            ?.value(state, action) ??\n        state;\n\nReducer<T> filterReducer<T>(Reducer<T> reducer, ReducerFilter<T> filter) {\n  return (reducer == null || filter == null)\n      ? reducer\n      : (T state, Action action) {\n          return filter(state, action) ? reducer(state, action) : state;\n        };\n}\n\nconst Object _SUB_EFFECT_RETURN_NULL = Object();\n\ntypedef SubEffect<T> = FutureOr<void> Function(Action action, Context<T> ctx);\n\n/// for action.type which override it's == operator\n/// return [UserEffecr]\nEffect<T> combineEffects<T>(Map<Object, SubEffect<T>> map) =>\n    (map == null || map.isEmpty)\n        ? null\n        : (Action action, Context<T> ctx) {\n            final SubEffect<T> subEffect = map.entries\n                .firstWhere(\n                  (MapEntry<Object, SubEffect<T>> entry) =>\n                      action.type == entry.key,\n                  orElse: () => null,\n                )\n                ?.value;\n\n            if (subEffect != null) {\n              return subEffect.call(action, ctx) ?? _SUB_EFFECT_RETURN_NULL;\n            }\n\n            //skip-lifecycle-actions\n            if (action.type is Lifecycle) {\n              return _SUB_EFFECT_RETURN_NULL;\n            }\n\n            /// no subEffect\n            return null;\n          };\n\n/// return [EffectDispatch]\nDispatch createEffectDispatch<T>(Effect<T> userEffect, Context<T> ctx) {\n  return (Action action) {\n    final Object result = userEffect?.call(action, ctx);\n\n    //skip-lifecycle-actions\n    if (action.type is Lifecycle && (result == null || result == false)) {\n      return _SUB_EFFECT_RETURN_NULL;\n    }\n\n    return result;\n  };\n}\n\n/// return [NextDispatch]\nDispatch createNextDispatch<T>(ContextSys<T> ctx) => (Action action) {\n      ctx.broadcastEffect(action, excluded: true);\n      ctx.store.dispatch(action);\n    };\n\n/// return [Dispatch]\nDispatch createDispatch<T>(Dispatch onEffect, Dispatch next, Context<T> ctx) =>\n    (Action action) {\n      final Object result = onEffect?.call(action);\n      if (result == null || result == false) {\n        next(action);\n      }\n\n      return result == _SUB_EFFECT_RETURN_NULL ? null : result;\n    };\n\nViewMiddleware<T> mergeViewMiddleware<T>(List<ViewMiddleware<T>> middleware) {\n  return Collections.reduce<ViewMiddleware<T>>(middleware,\n      (ViewMiddleware<T> first, ViewMiddleware<T> second) {\n    return (AbstractComponent<dynamic> component, Store<T> store) {\n      final Composable<ViewBuilder<dynamic>> inner = first(component, store);\n      final Composable<ViewBuilder<dynamic>> outer = second(component, store);\n      return (ViewBuilder<dynamic> view) {\n        return outer(inner(view));\n      };\n    };\n  });\n}\n\nAdapterMiddleware<T> mergeAdapterMiddleware<T>(\n    List<AdapterMiddleware<T>> middleware) {\n  return Collections.reduce<AdapterMiddleware<T>>(middleware,\n      (AdapterMiddleware<T> first, AdapterMiddleware<T> second) {\n    return (AbstractAdapter<dynamic> component, Store<T> store) {\n      final Composable<AdapterBuilder<dynamic>> inner = first(component, store);\n      final Composable<AdapterBuilder<dynamic>> outer =\n          second(component, store);\n      return (AdapterBuilder<dynamic> view) {\n        return outer(inner(view));\n      };\n    };\n  });\n}\n\nEffectMiddleware<T> mergeEffectMiddleware<T>(\n    List<EffectMiddleware<T>> middleware) {\n  return Collections.reduce<EffectMiddleware<T>>(middleware,\n      (EffectMiddleware<T> first, EffectMiddleware<T> second) {\n    return (AbstractLogic<dynamic> logic, Store<T> store) {\n      final Composable<Effect<dynamic>> inner = first(logic, store);\n      final Composable<Effect<dynamic>> outer = second(logic, store);\n      return (Effect<dynamic> effect) {\n        return outer(inner(effect));\n      };\n    };\n  });\n}\n"
  },
  {
    "path": "lib/src/redux_component/lifecycle.dart",
    "content": "import 'package:flutter/scheduler.dart';\n\nimport '../redux/redux.dart';\n\nenum Lifecycle {\n  /// componenmt(page) or adapter receives the following events\n  initState,\n  didChangeDependencies,\n  build,\n  reassemble,\n  didUpdateWidget,\n  deactivate,\n  dispose,\n  // didDisposed,\n\n  /// Only a adapter mixin VisibleChangeMixin will receive appear & disappear events.\n  /// class MyAdapter extends Adapter<T> with VisibleChangeMixin<T> {\n  ///   MyAdapter():super(\n  ///     ///\n  ///   );\n  /// }\n  appear,\n  disappear,\n\n  /// Only a componenmt(page) or adapter mixin WidgetsBindingObserverMixin will receive didChangeAppLifecycleState event.\n  /// class MyComponent extends Component<T> with WidgetsBindingObserverMixin<T> {\n  ///   MyComponent():super(\n  ///     ///\n  ///   );\n  /// }\n  didChangeAppLifecycleState,\n}\n\nclass LifecycleCreator {\n  static Action initState() => const Action(Lifecycle.initState);\n\n  static Action build(String name) => Action(Lifecycle.build, payload: name);\n\n  static Action reassemble() => const Action(Lifecycle.reassemble);\n\n  static Action dispose() => const Action(Lifecycle.dispose);\n\n  // static Action didDisposed() => const Action(Lifecycle.didDisposed);\n\n  static Action didUpdateWidget() => const Action(Lifecycle.didUpdateWidget);\n\n  static Action didChangeDependencies() =>\n      const Action(Lifecycle.didChangeDependencies);\n\n  static Action deactivate() => const Action(Lifecycle.deactivate);\n\n  static Action appear(int index) => Action(Lifecycle.appear, payload: index);\n\n  static Action disappear(int index) =>\n      Action(Lifecycle.disappear, payload: index);\n\n  static Action didChangeAppLifecycleState(AppLifecycleState state) =>\n      Action(Lifecycle.didChangeAppLifecycleState, payload: state);\n}\n"
  },
  {
    "path": "lib/src/redux_component/local.dart",
    "content": "import 'package:flutter/foundation.dart';\n\nimport 'basic.dart';\n\n///\n/// Description:\n///\n/// LocalProps的状态变化不会触发View的刷新\n///\n///\n/// Define:\n///\n/// ```dart\n/// class ComponentLocalProps extends LocalProps<ComponentLocalProps> {\n///   final TextEditingController controller = TextEditingController();\n///\n///   ComponentLocalProps(Context<Object> ctx) : super(ctx);\n///\n///   factory ComponentLocalProps.of(ExtraData ctx) {\n///     return LocalProps.provide((_) => ComponentLocalProps(_)).of(ctx);\n///   }\n///\n///   @override\n///   void destructor(Context<Object> ctx) {\n///     controller.dispose();\n///   }\n/// }\n///\n/// ```\n///\n/// Usage:\n///\n/// in View\n/// ```dart\n/// ComponentLocalProps.of(viewService).controller\n/// ```\n/// in effect\n/// ```dart\n/// ComponentLocalProps.of(ctx).controller\n/// ```\n///\nabstract class LocalProps<T extends LocalProps<T>> {\n  LocalProps(Context<Object> ctx) : assert(ctx != null);\n  void destructor(Context<Object> ctx);\n\n  static _LocalPropsProvider<T> provide<T extends LocalProps<T>>(\n          T Function(Context<Object>) construct) =>\n      _LocalPropsProvider<T>(\n        construct: construct,\n        destruct: (T local, Context<Object> ctx) => local.destructor(ctx),\n      );\n}\n\n@immutable\nclass _LocalPropsProvider<T> {\n  final T Function(Context<Object>) construct;\n  final void Function(T, Context<Object>) destruct;\n\n  const _LocalPropsProvider({@required this.construct, this.destruct})\n      : assert(construct != null,\n            'Please provide a constructor to create <T> instance.');\n\n  T of(ExtraData context) {\n    assert(context is Context<Object>);\n    final Context<Object> ctx = context;\n    if (ctx.extra[_key] == null) {\n      final T result = construct(ctx);\n      ctx.extra[_key] = result;\n      if (destruct != null) {\n        ctx.registerOnDisposed(() => destruct(result, ctx));\n      }\n    }\n    return ctx.extra[_key];\n  }\n\n  String get _key => '\\$ ${T.toString()}';\n}\n\n// class ComponentLocalProps extends LocalProps<ComponentLocalProps> {\n//   ComponentLocalProps(Context<Object> ctx) : super(ctx);\n\n//   factory ComponentLocalProps.of(ExtraData ctx) {\n//     return LocalProps.provide((_) => ComponentLocalProps(_)).of(ctx);\n//   }\n\n//   @override\n//   void destructor(Context<Object> ctx) {}\n// }\n"
  },
  {
    "path": "lib/src/redux_component/logic.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux/redux.dart';\nimport '../utils/utils.dart';\nimport 'basic.dart';\nimport 'dependencies.dart';\nimport 'helper.dart' as helper;\n\n/// Four parts\n/// 1. Reducer & ReducerFilter\n/// 2. Effect\n/// 3. Dependencies\n/// 4. Key\nabstract class Logic<T> implements AbstractLogic<T> {\n  final Reducer<T> _reducer;\n  final ReducerFilter<T> _filter;\n  final Effect<T> _effect;\n  final Dependencies<T> _dependencies;\n  final Object Function(T state) _key;\n\n  /// for extends\n  Reducer<T> get protectedReducer => _reducer;\n  ReducerFilter<T> get protectedFilter => _filter;\n  Effect<T> get protectedEffect => _effect;\n  Dependencies<T> get protectedDependencies => _dependencies;\n  Reducer<T> get protectedDependenciesReducer =>\n      protectedDependencies?.createReducer();\n  Object Function(T state) get protectedKey => _key;\n\n  /// Used as function cache to improve operational efficiency\n  final Map<String, Object> _resultCache = <String, Object>{};\n\n  Logic({\n    Reducer<T> reducer,\n    Dependencies<T> dependencies,\n    ReducerFilter<T> filter,\n    Effect<T> effect,\n\n    /// implement [StateKey] in T instead of using key in Logic.\n    /// class T implements StateKey {\n    ///   Object _key = UniqueKey();\n    ///   Object key() => _key;\n    /// }\n    @deprecated Object Function(T state) key,\n  })  : _reducer = reducer,\n        _filter = filter,\n        _effect = effect,\n        _dependencies = dependencies?.trim(),\n        // ignore:deprecated_member_use_from_same_package\n        assert(isAssignFrom<T, StateKey>() == false || key == null,\n            'Implements [StateKey] in T instead of using key in Logic.'),\n        _key = isAssignFrom<T, StateKey>()\n            // ignore:avoid_as\n            ? ((T state) => (state as StateKey).key())\n            // ignore:deprecated_member_use_from_same_package\n            : key;\n\n  @override\n  Type get propertyType => T;\n\n  bool isSuperTypeof<K>() => Tuple0<K>() is Tuple0<T>;\n\n  bool isTypeof<K>() => Tuple0<T>() is Tuple0<K>;\n\n  static bool isAssignFrom<P, Q>() => Tuple0<P>() is Tuple0<Q>;\n\n  /// if\n  /// _resultCache['key'] = null;\n  /// then\n  /// _resultCache.containsKey('key') will be true;\n  R cache<R>(String key, Get<R> getter) => _resultCache.containsKey(key)\n      ? _resultCache[key]\n      : (_resultCache[key] = getter());\n\n  @override\n  Reducer<T> createReducer() => helper.filterReducer(\n      combineReducers<T>(\n          <Reducer<T>>[protectedReducer, protectedDependenciesReducer]),\n      protectedFilter);\n\n  @override\n  Object onReducer(Object state, Action action) =>\n      cache<Reducer<T>>('onReducer', createReducer)?.call(state, action) ??\n      state;\n\n  @override\n  Dispatch createEffectDispatch(ContextSys<T> ctx, Enhancer<Object> enhancer) {\n    return helper.createEffectDispatch<T>(\n\n        /// enhance userEffect\n        enhancer.effectEnhance(\n          protectedEffect,\n          this,\n          ctx.store,\n        ),\n        ctx);\n  }\n\n  @override\n  Dispatch createNextDispatch(ContextSys<T> ctx, Enhancer<Object> enhancer) =>\n      helper.createNextDispatch<T>(ctx);\n\n  @override\n  Dispatch createDispatch(\n    Dispatch effectDispatch,\n    Dispatch nextDispatch,\n    Context<T> ctx,\n  ) =>\n      helper.createDispatch<T>(effectDispatch, nextDispatch, ctx);\n\n  @override\n  Object key(T state) => _key?.call(state) ?? ValueKey<Type>(runtimeType);\n\n  @override\n  Dependent<T> slot(String type) => protectedDependencies?.slot(type);\n\n  @override\n  Dependent<T> adapterDep() => protectedDependencies?.adapter;\n}\n"
  },
  {
    "path": "lib/src/redux_component/page.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux/redux.dart';\nimport 'basic.dart';\nimport 'batch_store.dart';\nimport 'component.dart';\nimport 'dependencies.dart';\nimport 'dispatch_bus.dart';\nimport 'enhancer.dart';\n\n/// init store's state by route-params\ntypedef InitState<T, P> = T Function(P params);\n\ntypedef StoreUpdater<T> = Store<T> Function(Store<T> store);\n\nfinal DispatchBus sharedBus = DispatchBusDefault();\n\n@immutable\nabstract class Page<T, P> extends Component<T> {\n  /// AppBus is a event-bus used to communicate between pages.\n  final DispatchBus appBus = sharedBus;\n\n  final InitState<T, P> _initState;\n\n  final Enhancer<T> enhancer;\n\n  /// connect with other stores\n  final List<StoreUpdater<T>> _storeUpdaters = <StoreUpdater<T>>[];\n\n  Page({\n    @required InitState<T, P> initState,\n    @required ViewBuilder<T> view,\n    Reducer<T> reducer,\n    ReducerFilter<T> filter,\n    Effect<T> effect,\n    Dependencies<T> dependencies,\n    ShouldUpdate<T> shouldUpdate,\n    WidgetWrapper wrapper,\n\n    /// implement [StateKey] in T instead of using key in Logic.\n    /// class T implements StateKey {\n    ///   Object _key = UniqueKey();\n    ///   Object key() => _key;\n    /// }\n    @deprecated Key Function(T) key,\n    List<Middleware<T>> middleware,\n    List<ViewMiddleware<T>> viewMiddleware,\n    List<EffectMiddleware<T>> effectMiddleware,\n    List<AdapterMiddleware<T>> adapterMiddleware,\n  })  : assert(initState != null),\n        _initState = initState,\n        enhancer = EnhancerDefault<T>(\n          middleware: middleware,\n          viewMiddleware: viewMiddleware,\n          effectMiddleware: effectMiddleware,\n          adapterMiddleware: adapterMiddleware,\n        ),\n        super(\n          view: view,\n          dependencies: dependencies,\n          reducer: reducer,\n          filter: filter,\n          effect: effect,\n          shouldUpdate: shouldUpdate,\n          wrapper: wrapper,\n          // ignore:deprecated_member_use_from_same_package\n          key: key,\n        );\n\n  Widget buildPage(P param) => protectedWrapper(_PageWidget<T, P>(\n        page: this,\n        param: param,\n      ));\n\n  Store<T> createStore(P param) => updateStore(createBatchStore<T>(\n        _initState(param),\n        createReducer(),\n        storeEnhancer: enhancer.storeEnhance,\n      ));\n\n  Store<T> updateStore(Store<T> store) => _storeUpdaters.fold(\n        store,\n        (Store<T> previousValue, StoreUpdater<T> element) =>\n            element(previousValue),\n      );\n\n  /// page-store connect with app-store\n  void connectExtraStore<K>(\n    Store<K> extraStore,\n\n    /// To solve Reducer<Object> is neither a subtype nor a supertype of Reducer<T> issue.\n    Object Function(Object, K) update,\n  ) =>\n      _storeUpdaters.add((Store<T> store) => connectStores<Object, K>(\n            store,\n            extraStore,\n            update,\n          ));\n\n  DispatchBus createPageBus() => DispatchBusDefault();\n\n  void unshift({\n    List<Middleware<T>> middleware,\n    List<ViewMiddleware<T>> viewMiddleware,\n    List<EffectMiddleware<T>> effectMiddleware,\n    List<AdapterMiddleware<T>> adapterMiddleware,\n  }) {\n    enhancer.unshift(\n      middleware: middleware,\n      viewMiddleware: viewMiddleware,\n      effectMiddleware: effectMiddleware,\n      adapterMiddleware: adapterMiddleware,\n    );\n  }\n\n  void append({\n    List<Middleware<T>> middleware,\n    List<ViewMiddleware<T>> viewMiddleware,\n    List<EffectMiddleware<T>> effectMiddleware,\n    List<AdapterMiddleware<T>> adapterMiddleware,\n  }) {\n    enhancer.append(\n      middleware: middleware,\n      viewMiddleware: viewMiddleware,\n      effectMiddleware: effectMiddleware,\n      adapterMiddleware: adapterMiddleware,\n    );\n  }\n}\n\nclass _PageWidget<T, P> extends StatefulWidget {\n  final Page<T, P> page;\n  final P param;\n\n  const _PageWidget({\n    Key key,\n    @required this.page,\n    @required this.param,\n  }) : super(key: key);\n\n  @override\n  State<StatefulWidget> createState() => _PageState<T, P>();\n}\n\nclass _PageState<T, P> extends State<_PageWidget<T, P>> {\n  Store<T> _store;\n  DispatchBus _pageBus;\n\n  final Map<String, Object> extra = <String, Object>{};\n\n  @override\n  void initState() {\n    super.initState();\n    _store = widget.page.createStore(widget.param);\n    _pageBus = widget.page.createPageBus();\n  }\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n\n    /// Register inter-page broadcast\n    _pageBus.attach(widget.page.appBus);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    // return PageProvider(\n    //   store: _store,\n    //   extra: extra,\n    //   child:\n    // );\n\n    return widget.page.buildComponent(\n      _store,\n      _store.getState,\n      bus: _pageBus,\n      enhancer: widget.page.enhancer,\n    );\n  }\n\n  @override\n  void dispose() {\n    _pageBus.detach();\n    _store.teardown();\n    super.dispose();\n  }\n}\n\n@deprecated\nclass PageProvider extends InheritedWidget {\n  final Store<Object> store;\n\n  /// Used to store page data if needed\n  final Map<String, Object> extra;\n\n  const PageProvider({\n    @required this.store,\n    @required this.extra,\n    @required Widget child,\n    Key key,\n  })  : assert(store != null),\n        assert(child != null),\n        super(child: child, key: key);\n\n  static PageProvider tryOf(BuildContext context) {\n    final PageProvider provider =\n        context.dependOnInheritedWidgetOfExactType<PageProvider>();\n    return provider;\n  }\n\n  @override\n  bool updateShouldNotify(PageProvider oldWidget) =>\n      store != oldWidget.store && extra != oldWidget.extra;\n}\n"
  },
  {
    "path": "lib/src/redux_component/redux_component.dart",
    "content": "export 'auto_dispose.dart';\nexport 'basic.dart';\nexport 'batch_store.dart';\nexport 'component.dart';\nexport 'context.dart';\nexport 'dependencies.dart';\nexport 'dependent.dart';\nexport 'dispatch_bus.dart';\nexport 'enhancer.dart';\nexport 'helper.dart';\nexport 'lifecycle.dart';\nexport 'local.dart';\nexport 'logic.dart';\nexport 'page.dart';\n"
  },
  {
    "path": "lib/src/redux_component_mixin/keep_alive_mixin.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux_component/redux_component.dart';\n\n/// usage\n/// class MyComponent extends Component<T> with KeepAliveMixin<T> {\n///   MyComponent():super(\n///     ///\n///   );\n/// }\n/// Only For [Component]\nmixin KeepAliveMixin<T> on Component<T> {\n  @override\n  ComponentState<T> createState() => _KeepAliveStfState<T>();\n}\n\nclass _KeepAliveStfState<T> extends ComponentState<T>\n    with AutomaticKeepAliveClientMixin {\n  @override\n  bool get wantKeepAlive => true;\n\n  @override\n  Widget build(BuildContext context) {\n    super.build(context);\n    return ctx.buildWidget();\n  }\n}\n\n/// usage\n/// class MyComponent extends Component<T> {\n///   MyComponent():super(\n///     wrapper: keepAliveClientWrapper,\n///   );\n/// }\n/// For Both [Component] & [Page]\nWidget keepAliveClientWrapper(Widget child) => _KeepAliveWidget(child);\n\nclass _KeepAliveWidget extends StatefulWidget {\n  final Widget child;\n\n  const _KeepAliveWidget(this.child);\n\n  @override\n  State<StatefulWidget> createState() => _KeepAliveState();\n}\n\nclass _KeepAliveState extends State<_KeepAliveWidget>\n    with AutomaticKeepAliveClientMixin {\n  @override\n  bool get wantKeepAlive => true;\n\n  @override\n  Widget build(BuildContext context) {\n    super.build(context);\n    return widget.child;\n  }\n}\n"
  },
  {
    "path": "lib/src/redux_component_mixin/private_reducer_mixin.dart",
    "content": "import '../redux/redux.dart';\nimport '../redux_component/redux_component.dart';\n\n/// usage\n/// class MyComponent extends Component<T> with PrivateReducerMixin<T> {\n///   MyComponent():super(\n///     ///\n///   );\n/// }\nmixin PrivateReducerMixin<T> on Logic<T> {\n  @override\n  Reducer<T> get protectedReducer {\n    final Reducer<T> superReducer = super.protectedReducer;\n    return superReducer != null\n        ? (T state, Action action) {\n            if (action is PrivateAction && action.target == state) {\n              return superReducer(state, action.asAction());\n            }\n            return state;\n          }\n        : null;\n  }\n\n  @override\n  Dispatch createDispatch(Dispatch effect, Dispatch next, Context<T> ctx) {\n    final Dispatch superDispatch = super.createDispatch(effect, next, ctx);\n    return (Action action) {\n      if (action.type is! Lifecycle && action is! PrivateAction) {\n        action = PrivateAction(\n          action.type,\n          payload: action.payload,\n          target: ctx.state,\n        );\n      }\n      return superDispatch(action);\n    };\n  }\n}\n\nclass PrivateAction extends Action {\n  final Object target;\n  PrivateAction(Object type, {dynamic payload, this.target})\n      : super(type, payload: payload);\n\n  Action asAction() => Action(type, payload: payload);\n}\n"
  },
  {
    "path": "lib/src/redux_component_mixin/redux_component_mixin.dart",
    "content": "export 'keep_alive_mixin.dart';\nexport 'private_reducer_mixin.dart';\nexport 'single_ticker_provider_mixin.dart';\nexport 'ticker_provider_mixin.dart';\nexport 'visible_change_mixin.dart';\nexport 'widgets_binding_observer_mixin.dart';\n"
  },
  {
    "path": "lib/src/redux_component_mixin/single_ticker_provider_mixin.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux_component/redux_component.dart';\n\n/// usage\n/// class MyComponent extends Component<T> with SingleTickerProviderMixin<T> {\n///   MyComponent():super(\n///     ///\n///   );\n/// }\n/// For Both [Component] & [Page]\nmixin SingleTickerProviderMixin<T> on Component<T> {\n  @override\n  _SingleTickerProviderStfState<T> createState() =>\n      _SingleTickerProviderStfState<T>();\n}\n\nclass _SingleTickerProviderStfState<T> extends ComponentState<T>\n    with SingleTickerProviderStateMixin {\n  /// fix SingleTickerProviderStateMixin dispose bug\n  @override\n  void dispose() {\n    disposeCtx();\n    super.dispose();\n  }\n}\n"
  },
  {
    "path": "lib/src/redux_component_mixin/ticker_provider_mixin.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux_component/redux_component.dart';\n\n/// usage\n/// class MyComponent extends Component<T> with TickerProviderMixin<T> {\n///   MyComponent():super(\n///     ///\n///   );\n/// }\n/// For Both [Component] & [Page]\nmixin TickerProviderMixin<T> on Component<T> {\n  @override\n  _TickerProviderStfState<T> createState() => _TickerProviderStfState<T>();\n}\n\nclass _TickerProviderStfState<T> extends ComponentState<T>\n    with TickerProviderStateMixin {\n  /// fix TickerProviderStateMixin dispose bug\n  @override\n  void dispose() {\n    disposeCtx();\n    super.dispose();\n  }\n}\n"
  },
  {
    "path": "lib/src/redux_component_mixin/visible_change_mixin.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux/redux.dart';\nimport '../redux_component/redux_component.dart';\n\nimport '../utils/utils.dart';\n\n/// usage\n/// class MyAdapter extends Adapter<T> with VisibleChangeMixin<T> {\n///   MyAdapter():super(\n///     ///\n///   );\n/// }\nmixin VisibleChangeMixin<T> on AbstractAdapter<T> {\n  @override\n  ListAdapter buildAdapter(ContextSys<T> ctx) {\n    return _wrapVisibleChange<T>(super.buildAdapter(ctx), ctx);\n  }\n}\n\nclass _VisibleChangeState extends State<_VisibleChangeWidget> {\n  @override\n  Widget build(BuildContext context) =>\n      widget.itemBuilder(context, widget.index);\n\n  @override\n  void initState() {\n    super.initState();\n    widget.dispatch(LifecycleCreator.appear(widget.index));\n  }\n\n  @override\n  void dispose() {\n    widget.dispatch(LifecycleCreator.disappear(widget.index));\n    super.dispose();\n  }\n}\n\nclass _VisibleChangeWidget extends StatefulWidget {\n  final IndexedWidgetBuilder itemBuilder;\n  final int index;\n  final Dispatch dispatch;\n\n  const _VisibleChangeWidget({\n    Key key,\n    this.itemBuilder,\n    this.index,\n    this.dispatch,\n  }) : super(key: key);\n\n  @override\n  State<StatefulWidget> createState() => _VisibleChangeState();\n}\n\nListAdapter _wrapVisibleChange<T>(\n  ListAdapter listAdapter,\n  LogicContext<T> ctx,\n) {\n  final _VisibleChangeDispatch onChange =\n      (ctx.extra['\\$visible'] ??= _VisibleChangeDispatch(ctx.dispatch));\n\n  return listAdapter == null\n      ? null\n      : ListAdapter(\n          (BuildContext buildContext, int index) => _VisibleChangeWidget(\n            itemBuilder: listAdapter.itemBuilder,\n            index: index,\n            dispatch: onChange.onAction,\n            key: ValueKey<Tuple2<Object, int>>(Tuple2<Object, int>(ctx, index)),\n          ),\n          listAdapter.itemCount,\n        );\n}\n\nclass _VisibleChangeDispatch extends AutoDispose {\n  int _appearsCount = 0;\n  final Dispatch dispatch;\n\n  _VisibleChangeDispatch(this.dispatch);\n\n  void onAction(Action action) {\n    if (action.type == Lifecycle.appear) {\n      assert(_appearsCount >= 0);\n      if (_appearsCount == 0) {\n        if (!isDisposed) {\n          dispatch(action);\n        }\n      }\n      _appearsCount++;\n    } else if (action.type == Lifecycle.disappear) {\n      _appearsCount--;\n      assert(_appearsCount >= 0);\n      if (_appearsCount == 0) {\n        if (!isDisposed) {\n          dispatch(action);\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/src/redux_component_mixin/widgets_binding_observer_mixin.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux_component/redux_component.dart';\n\n/// usage\n/// class MyComponent extends Component<T> with WidgetsBindingObserverMixin<T> {\n///   MyComponent():super(\n///     ///\n///   );\n/// }\n/// For Both [Component] & [Page]\nmixin WidgetsBindingObserverMixin<T> on Component<T> {\n  @override\n  _WidgetsBindingObserverStfState<T> createState() =>\n      _WidgetsBindingObserverStfState<T>();\n}\n\nclass _WidgetsBindingObserverStfState<T> extends ComponentState<T>\n    with WidgetsBindingObserver {\n  @override\n  void initState() {\n    super.initState();\n    WidgetsBinding.instance.addObserver(this);\n  }\n\n  @override\n  void dispose() {\n    WidgetsBinding.instance.removeObserver(this);\n    super.dispose();\n  }\n\n  @override\n  void didChangeAppLifecycleState(AppLifecycleState state) {\n    super.didChangeAppLifecycleState(state);\n    ctx.onLifecycle(LifecycleCreator.didChangeAppLifecycleState(state));\n  }\n}\n"
  },
  {
    "path": "lib/src/redux_connector/connector.dart",
    "content": "import '../redux/redux.dart';\n\nimport 'op_mixin.dart';\n\nclass ConnOp<T, P> extends MutableConn<T, P> with ConnOpMixin<T, P> {\n  final P Function(T) _getter;\n  final void Function(T, P) _setter;\n\n  const ConnOp({\n    P Function(T) get,\n    void Function(T, P) set,\n  })  : _getter = get,\n        _setter = set;\n\n  @override\n  P get(T state) => _getter(state);\n\n  @override\n  void set(T state, P subState) => _setter(state, subState);\n}\n"
  },
  {
    "path": "lib/src/redux_connector/generator.dart",
    "content": "String Function() generator() {\n  int nextId = 0;\n  String prefix = '';\n  return () {\n    /// fix '0x3FFFFFFFFFFFFFFF' can't be represented exactly in JavaScript.\n    if (++nextId >= 0x3FFFFFFF) {\n      nextId = 0;\n      prefix = '\\$' + prefix;\n    }\n    return prefix + nextId.toString();\n  };\n}\n"
  },
  {
    "path": "lib/src/redux_connector/helper.dart",
    "content": "import '../redux/redux.dart';\nimport '../redux_component/redux_component.dart';\n\nclass ConnHelper {\n  static AbstractConnector<T, K> to<T, P, K>(\n      AbstractConnector<T, P> one, AbstractConnector<P, K> two) {\n    return _AbstractConnector<T, P, K>(one, two);\n  }\n\n  static Dependent<T> join<T, P>(\n          AbstractConnector<T, P> conn, AbstractLogic<P> logic) =>\n      createDependent<T, P>(conn, logic);\n}\n\nclass _AbstractConnector<T, P, K> extends AbstractConnector<T, K> {\n  final AbstractConnector<T, P> one;\n  final AbstractConnector<P, K> two;\n\n  _AbstractConnector(this.one, this.two);\n\n  @override\n  K get(T state) {\n    return two.get(one.get(state));\n  }\n\n  @override\n  SubReducer<T> subReducer(Reducer<K> reducer) {\n    return one.subReducer((P state, Action action) {\n      return two.subReducer(reducer)(state, action, false);\n    });\n  }\n}\n"
  },
  {
    "path": "lib/src/redux_connector/map_like.dart",
    "content": "import 'connector.dart';\nimport 'generator.dart';\n\nabstract class MapLike {\n  Map<String, Object> _fieldsMap = <String, Object>{};\n\n  void clear() => _fieldsMap.clear();\n\n  Object operator [](String key) => _fieldsMap[key];\n\n  void operator []=(String key, Object value) => _fieldsMap[key] = value;\n\n  bool containsKey(String key) => _fieldsMap.containsKey(key);\n\n  void copyFrom(MapLike from) =>\n      _fieldsMap = <String, Object>{}..addAll(from._fieldsMap);\n}\n\nConnOp<T, P> withMapLike<T extends MapLike, P>(String key) => ConnOp<T, P>(\n      get: (T state) => state[key],\n      set: (T state, P sub) => state[key] = sub,\n    );\n\nclass AutoInitConnector<T extends MapLike, P> extends ConnOp<T, P> {\n  static final String Function() _gen = generator();\n\n  final String _key;\n  final void Function(T state, P sub) _setHook;\n  final P Function(T state) init;\n\n  AutoInitConnector(this.init, {String key, void set(T state, P sub)})\n      : assert(init != null),\n        _setHook = set,\n        _key = key ?? _gen();\n\n  @override\n  P get(T state) =>\n      state.containsKey(_key) ? state[_key] : (state[_key] = init(state));\n\n  @override\n  void set(T state, P subState) {\n    state[_key] = subState;\n    _setHook?.call(state, subState);\n  }\n}\n"
  },
  {
    "path": "lib/src/redux_connector/none.dart",
    "content": "import '../redux/redux.dart';\nimport 'op_mixin.dart';\n\nclass NoneConn<T> extends ImmutableConn<T, T> with ConnOpMixin<T, T> {\n  const NoneConn();\n\n  @override\n  T get(T state) => state;\n\n  @override\n  T set(T state, T subState) => subState;\n}\n"
  },
  {
    "path": "lib/src/redux_connector/op_mixin.dart",
    "content": "import '../redux/redux.dart';\nimport '../redux_component/redux_component.dart';\n\nmixin ConnOpMixin<T, P> on AbstractConnector<T, P> {\n  Dependent<T> operator +(AbstractLogic<P> logic) =>\n      createDependent<T, P>(this, logic);\n}\n"
  },
  {
    "path": "lib/src/redux_connector/redux_connector.dart",
    "content": "export 'connector.dart';\nexport 'helper.dart';\nexport 'map_like.dart';\nexport 'none.dart';\nexport 'op_mixin.dart';\nexport 'reselect.dart';\n"
  },
  {
    "path": "lib/src/redux_connector/reselect.dart",
    "content": "import 'package:flutter/foundation.dart';\n\nimport '../redux/redux.dart';\nimport 'op_mixin.dart';\n\nbool _listEquals(List<dynamic> list1, List<dynamic> list2) {\n  if (identical(list1, list2)) {\n    return true;\n  }\n  if (list1 == null || list2 == null) {\n    return false;\n  }\n  final int length = list1.length;\n  if (length != list2.length) {\n    return false;\n  }\n  for (int i = 0; i < length; i++) {\n    final dynamic e1 = list1[i], e2 = list2[i];\n    if (e1 != e2) {\n      if (e1 is List && e1.runtimeType == e2?.runtimeType) {\n        if (!_listEquals(e1, e2)) {\n          return false;\n        }\n      }\n      return false;\n    }\n  }\n  return true;\n}\n\nabstract class _BasicReselect<T, P> extends MutableConn<T, P>\n    with ConnOpMixin<T, P> {\n  List<dynamic> _subsCache;\n  P _pCache;\n  bool _hasBeenCalled = false;\n\n  List<dynamic> getSubs(T state);\n\n  P reduceSubs(List<dynamic> list);\n\n  @override\n  P get(T state) {\n    final List<dynamic> subs = getSubs(state);\n    if (!_hasBeenCalled || !_listEquals(subs, _subsCache)) {\n      _subsCache = subs;\n      _pCache = reduceSubs(_subsCache);\n      _hasBeenCalled = true;\n    }\n    return _pCache;\n  }\n}\n\nabstract class Reselect1<T, P, K0> extends _BasicReselect<T, P> {\n  K0 getSub0(T state);\n  P computed(K0 state);\n\n  @override\n  List<dynamic> getSubs(T state) => <dynamic>[getSub0(state)];\n\n  @override\n  P reduceSubs(List<dynamic> list) => Function.apply(computed, list);\n}\n\nabstract class Reselect2<T, P, K0, K1> extends _BasicReselect<T, P> {\n  K0 getSub0(T state);\n  K1 getSub1(T state);\n  P computed(K0 sub0, K1 sub1);\n\n  @override\n  List<dynamic> getSubs(T state) => <dynamic>[getSub0(state), getSub1(state)];\n\n  @override\n  P reduceSubs(List<dynamic> list) => Function.apply(computed, list);\n}\n\nabstract class Reselect3<T, P, K0, K1, K2> extends _BasicReselect<T, P> {\n  K0 getSub0(T state);\n  K1 getSub1(T state);\n  K2 getSub2(T state);\n  P computed(K0 sub0, K1 sub1, K2 sub2);\n\n  @override\n  List<dynamic> getSubs(T state) => <dynamic>[\n        getSub0(state),\n        getSub1(state),\n        getSub2(state),\n      ];\n\n  @override\n  P reduceSubs(List<dynamic> list) => Function.apply(computed, list);\n}\n\nabstract class Reselect4<T, P, K0, K1, K2, K3> extends _BasicReselect<T, P> {\n  K0 getSub0(T state);\n  K1 getSub1(T state);\n  K2 getSub2(T state);\n  K3 getSub3(T state);\n  P computed(K0 sub0, K1 sub1, K2 sub2, K3 sub3);\n\n  @override\n  List<dynamic> getSubs(T state) => <dynamic>[\n        getSub0(state),\n        getSub1(state),\n        getSub2(state),\n        getSub3(state),\n      ];\n\n  @override\n  P reduceSubs(List<dynamic> list) => Function.apply(computed, list);\n}\n\nabstract class Reselect5<T, P, K0, K1, K2, K3, K4>\n    extends _BasicReselect<T, P> {\n  K0 getSub0(T state);\n  K1 getSub1(T state);\n  K2 getSub2(T state);\n  K3 getSub3(T state);\n  K4 getSub4(T state);\n  P computed(K0 sub0, K1 sub1, K2 sub2, K3 sub3, K4 sub4);\n\n  @override\n  List<dynamic> getSubs(T state) => <dynamic>[\n        getSub0(state),\n        getSub1(state),\n        getSub2(state),\n        getSub3(state),\n        getSub4(state),\n      ];\n\n  @override\n  P reduceSubs(List<dynamic> list) => Function.apply(computed, list);\n}\n\nabstract class Reselect6<T, P, K0, K1, K2, K3, K4, K5>\n    extends _BasicReselect<T, P> {\n  K0 getSub0(T state);\n  K1 getSub1(T state);\n  K2 getSub2(T state);\n  K3 getSub3(T state);\n  K4 getSub4(T state);\n  K5 getSub5(T state);\n  P computed(K0 sub0, K1 sub1, K2 sub2, K3 sub3, K4 sub4, K5 sub5);\n\n  @override\n  List<dynamic> getSubs(T state) => <dynamic>[\n        getSub0(state),\n        getSub1(state),\n        getSub2(state),\n        getSub3(state),\n        getSub4(state),\n        getSub5(state),\n      ];\n\n  @override\n  P reduceSubs(List<dynamic> list) => Function.apply(computed, list);\n}\n\nabstract class Reselect<T, P> extends _BasicReselect<T, P> {\n  P computed(List<dynamic> list);\n\n  @override\n  P reduceSubs(List<dynamic> list) => Function.apply(computed, list);\n}\n\n/// issue [https://github.com/alibaba/fish-redux/issues/482]\nmixin ReselectMixin<T, P> on MutableConn<T, P> {\n  List<dynamic> _cachedFactors;\n  P _cachedResult;\n  bool _hasBeenCalled = false;\n\n  P computed(T state);\n\n  List<dynamic> factors(T state) => <dynamic>[state];\n\n  @mustCallSuper\n  @override\n  P get(T state) {\n    final List<dynamic> newFactors = factors(state);\n    if (!_hasBeenCalled || !_listEquals(newFactors, _cachedFactors)) {\n      _cachedFactors = newFactors.toList(growable: false);\n      _cachedResult = computed(state);\n      _hasBeenCalled = true;\n    }\n    return _cachedResult;\n  }\n}\n"
  },
  {
    "path": "lib/src/redux_middleware/adapter_middleware/adapter_middleware.dart",
    "content": "export 'safety_adapter.dart';\n"
  },
  {
    "path": "lib/src/redux_middleware/adapter_middleware/safety_adapter.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../../redux/redux.dart';\nimport '../../redux_component/redux_component.dart';\nimport '../../utils/utils.dart';\n\n/// type = {0, 1}\nAdapterMiddleware<T> safetyAdapter<T>({\n  Widget Function(dynamic, StackTrace,\n          {AbstractAdapter<dynamic> adapter, Store<T> store, int type})\n      onError,\n}) {\n  return (AbstractAdapter<dynamic> adapter, Store<T> store) {\n    return (AdapterBuilder<dynamic> next) {\n      return isDebug()\n          ? next\n          : (dynamic state, Dispatch dispatch, ViewService viewService) {\n              try {\n                final ListAdapter result = next(state, dispatch, viewService);\n                return ListAdapter((BuildContext buildContext, int index) {\n                  try {\n                    return result.itemBuilder(buildContext, index);\n                  } catch (e, stackTrace) {\n                    return onError?.call(\n                          e,\n                          stackTrace,\n                          adapter: adapter,\n                          store: store,\n                          type: 1,\n                        ) ??\n                        Container(width: 0, height: 0);\n                  }\n                }, result.itemCount);\n              } catch (e, stackTrace) {\n                final Widget errorWidget = onError?.call(\n                  e,\n                  stackTrace,\n                  adapter: adapter,\n                  store: store,\n                  type: 0,\n                );\n                return errorWidget == null\n                    ? const ListAdapter(null, 0)\n                    : ListAdapter(\n                        (BuildContext buildContext, int index) => errorWidget,\n                        1);\n              }\n            };\n    };\n  };\n}\n"
  },
  {
    "path": "lib/src/redux_middleware/middleware/log.dart",
    "content": "import '../../redux/redux.dart';\nimport '../../utils/utils.dart';\n\n/// Middleware for print action dispatch.\n/// It works on debug mode.\nMiddleware<T> logMiddleware<T>({\n  String tag = 'redux',\n  String Function(T) monitor,\n}) {\n  return ({Dispatch dispatch, Get<T> getState}) {\n    return (Dispatch next) {\n      return isDebug()\n          ? (Action action) {\n              print('---------- [$tag] ----------');\n              print('[$tag] ${action.type} ${action.payload}');\n\n              final T prevState = getState();\n              if (monitor != null) {\n                print('[$tag] prev-state: ${monitor(prevState)}');\n              }\n\n              next(action);\n\n              final T nextState = getState();\n              if (monitor != null) {\n                print('[$tag] next-state: ${monitor(nextState)}');\n              }\n\n              // if (prevState == nextState) {\n              //   print('[$tag] warning: ${action.type} has not been used.');\n              // }\n\n              print('========== [$tag] ================');\n            }\n          : next;\n    };\n  };\n}\n"
  },
  {
    "path": "lib/src/redux_middleware/middleware/middleware.dart",
    "content": "export 'log.dart';\nexport 'performance.dart';\n"
  },
  {
    "path": "lib/src/redux_middleware/middleware/performance.dart",
    "content": "import '../../redux/redux.dart';\nimport '../../utils/utils.dart';\n\n/// Middleware for print action dispatch performance by time consuming.\n/// It works on debug mode.\nMiddleware<T> performanceMiddleware<T>({String tag = 'redux'}) {\n  return ({Dispatch dispatch, Get<T> getState}) {\n    return (Dispatch next) {\n      return isDebug()\n          ? (Action action) {\n              final int markPrev = DateTime.now().microsecondsSinceEpoch;\n              next(action);\n              final int markNext = DateTime.now().microsecondsSinceEpoch;\n              print('$tag performance: ${action.type} ${markNext - markPrev}');\n            }\n          : next;\n    };\n  };\n}\n"
  },
  {
    "path": "lib/src/redux_middleware/redux_middleware.dart",
    "content": "export 'adapter_middleware/adapter_middleware.dart';\nexport 'middleware/middleware.dart';\nexport 'view_middleware/view_middleware.dart';\n"
  },
  {
    "path": "lib/src/redux_middleware/view_middleware/safety_view.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../../redux/redux.dart';\nimport '../../redux_component/redux_component.dart';\nimport '../../utils/utils.dart';\n\nViewMiddleware<T> safetyView<T>(\n    {Widget Function(dynamic, StackTrace,\n            {AbstractComponent<dynamic> component, Store<T> store})\n        onError}) {\n  return (AbstractComponent<dynamic> component, Store<T> store) {\n    return (ViewBuilder<dynamic> next) {\n      return isDebug()\n          ? next\n          : (dynamic state, Dispatch dispatch, ViewService viewService) {\n              try {\n                return next(state, dispatch, viewService);\n              } catch (e, stackTrace) {\n                return onError?.call(\n                      e,\n                      stackTrace,\n                      component: component,\n                      store: store,\n                    ) ??\n                    Container(width: 0, height: 0);\n              }\n            };\n    };\n  };\n}\n"
  },
  {
    "path": "lib/src/redux_middleware/view_middleware/view_middleware.dart",
    "content": "export 'safety_view.dart';\n"
  },
  {
    "path": "lib/src/redux_routes/page_routes.dart",
    "content": "import 'package:flutter/widgets.dart' hide Action, Page;\n\nimport '../redux_component/redux_component.dart';\n\n/// Define a basic behavior of routes.\nabstract class AbstractRoutes {\n  Widget buildPage(String path, dynamic arguments);\n}\n\n/// Each page has a unique store.\n@immutable\nclass PageRoutes implements AbstractRoutes {\n  final Map<String, Page<Object, dynamic>> pages;\n\n  PageRoutes({\n    @required this.pages,\n\n    /// For common enhance\n    void Function(String, Page<Object, dynamic>) visitor,\n  }) : assert(pages != null, 'Expected the pages to be non-null value.') {\n    if (visitor != null) {\n      pages.forEach(visitor);\n    }\n  }\n\n  @override\n  Widget buildPage(String path, dynamic arguments) =>\n      pages[path]?.buildPage(arguments);\n}\n"
  },
  {
    "path": "lib/src/redux_routes/redux_routes.dart",
    "content": "export 'page_routes.dart';\n"
  },
  {
    "path": "lib/src/utils/collections.dart",
    "content": "import 'dart:core';\n\n/// Util for collections.\nclass Collections {\n  /// Wrap List.reduce with a check list is null or empty.\n  static E reduce<E>(List<E> list, E combine(E e0, E e1)) =>\n      (list == null || list.isEmpty) ? null : list.reduce(combine);\n\n  /// Wrap List.fold with a check list is null or empty.\n  static T fold<T, E>(T init, List<E> list, T combine(T e0, E e1)) =>\n      (list == null || list.isEmpty) ? init : list.fold(init, combine);\n\n  /// Flatten list\n  /// For example:\n  /// List<String> a = ['a', 'b', 'c'];\n  /// List<String> b = ['1', '2', '3'];\n  /// List<List<String>> list = [a, b] // [[a, b, c], [1, 2, 3]]\n  /// List<String> listFlatten = Collections.flatten(list) // [a, b, c, 1, 2, 3]\n  static List<E> flatten<E>(List<List<E>> lists) => reduce(lists, merge);\n\n  /// Merge two Iterable\n  /// List<String> a = ['a', 'b', 'c'];\n  /// List<String> b = ['1', '2', '3'];\n  /// List<String> listMerge = Collections.merge(a, b) // [a, b, c, 1, 2, 3]\n  static List<T> merge<T>(Iterable<T> a, Iterable<T> b) =>\n      <T>[]..addAll(a ?? <T>[])..addAll(b ?? <T>[]);\n\n  static List<T> clone<T>(Iterable<T> a) =>\n      (a == null || a.isEmpty) ? <T>[] : (<T>[]..addAll(a));\n\n  /// Cast map to list\n  /// Map<String, String> map = {'key0': 'a', 'key1': 'b', 'key2': 'c'};\n  /// Function mapFunction = (String value, String key) => value;\n  /// List<String> list = Collections.castMapToList<String, String, String>(\n  ///        map, mapFunction); // [a, b, c]\n  static List<T> castMapToList<T, K, V>(Map<K, V> map0, T map(V v, K k)) =>\n      map0.entries\n          .map((MapEntry<K, V> entry) => map(entry.value, entry.key))\n          .toList();\n\n  /// Cast map with a map function\n  static Map<K, V1> castMap<K, V0, V1>(Map<K, V0> map0, V1 map(V0 v0, K k)) =>\n      <K, V1>{}..addEntries(castMapToList<MapEntry<K, V1>, K, V0>(\n          map0, (V0 v, K k) => MapEntry<K, V1>(k, map(v, k))));\n\n  /// Emit item null and return new list.\n  /// List<String> list = ['1', '2', null, '3', null];\n  /// print(list)                       // [1, 2, null, 3, null]\n  /// print(Collections.compact(list)); // [1, 2, 3]\n  static List<T> compact<T>(Iterable<T> list, {bool growable = true}) =>\n      list?.where((T e) => e != null)?.toList(growable: growable);\n\n  /// Check if an Object is Empty.\n  static bool isEmpty(Object value) {\n    if (value == null) {\n      return true;\n    } else {\n      if (value is String) {\n        return value.isEmpty;\n      } else if (value is List) {\n        return value.isEmpty;\n      } else if (value is Map) {\n        return value.isEmpty;\n      } else if (value is Set) {\n        return value.isEmpty;\n      } else {\n        return false;\n      }\n    }\n  }\n\n  static bool isNotEmpty(Object value) => !isEmpty(value);\n}\n"
  },
  {
    "path": "lib/src/utils/debug.dart",
    "content": "bool _debugFlag = false;\n\n/// Is app run a debug mode.\nbool isDebug() {\n  /// Assert statements have no effect in production code;\n  /// they’re for development only. Flutter enables asserts in debug mode.\n  assert(() {\n    _debugFlag = true;\n    return _debugFlag;\n  }());\n  return _debugFlag;\n}\n\n/// wrap println with bool return.\nbool println(Object object) {\n  print(object);\n  return true;\n}\n"
  },
  {
    "path": "lib/src/utils/hash.dart",
    "content": "/// Jenkins hash function, optimized for small integers.\n///\n/// Borrowed from the dart sdk: sdk/lib/math/jenkins_smi_hash.dart.\nint hash(Iterable<int> values) {\n  int hash = 0;\n\n  /// combine\n  for (int value in values) {\n    hash = 0x1fffffff & (hash + value);\n    hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));\n    hash = hash ^ (hash >> 6);\n  }\n\n  /// finish\n  hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));\n  hash = hash ^ (hash >> 11);\n  return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));\n}\n"
  },
  {
    "path": "lib/src/utils/tuple.dart",
    "content": "import 'hash.dart';\n\nclass Tuple0<T0> {\n  const Tuple0();\n\n  @override\n  bool operator ==(Object other) => other is Tuple0;\n\n  @override\n  int get hashCode => hash(<int>[0]);\n}\n\n/// Represents a 1-tuple\n/// Mutable data types are easier to use.\nclass Tuple1<T0> {\n  T0 i0;\n\n  Tuple1([this.i0]);\n\n  @override\n  String toString() => '[$i0]';\n\n  @override\n  bool operator ==(Object other) => other is Tuple1 && other.i0 == i0;\n\n  @override\n  int get hashCode => hash(<int>[i0.hashCode]);\n}\n\n/// Represents a 2-tuple or pair.\nclass Tuple2<T0, T1> {\n  /// First item of the tuple\n  T0 i0;\n\n  /// Second item of the tuple\n  T1 i1;\n\n  /// Create a new tuple value with the specified items.\n  Tuple2([this.i0, this.i1]);\n\n  @override\n  String toString() => '[$i0, $i1]';\n\n  @override\n  bool operator ==(Object other) =>\n      other is Tuple2 && other.i0 == i0 && other.i1 == i1;\n\n  @override\n  int get hashCode => hash(<int>[i0.hashCode, i1.hashCode]);\n}\n\n/// Represents a 3-tuple or pair.\nclass Tuple3<T0, T1, T2> {\n  T0 i0;\n  T1 i1;\n  T2 i2;\n\n  Tuple3([this.i0, this.i1, this.i2]);\n\n  @override\n  String toString() => '[$i0, $i1, $i2]';\n\n  @override\n  bool operator ==(Object other) =>\n      other is Tuple3 && other.i0 == i0 && other.i1 == i1 && other.i2 == i2;\n\n  @override\n  int get hashCode => hash(<int>[i0.hashCode, i1.hashCode, i2.hashCode]);\n}\n\n/// Represents a 4-tuple or pair.\nclass Tuple4<T0, T1, T2, T3> {\n  T0 i0;\n  T1 i1;\n  T2 i2;\n  T3 i3;\n\n  Tuple4([this.i0, this.i1, this.i2, this.i3]);\n\n  @override\n  String toString() => '[$i0, $i1, $i2, $i3]';\n\n  @override\n  bool operator ==(Object other) =>\n      other is Tuple4 &&\n      other.i0 == i0 &&\n      other.i1 == i1 &&\n      other.i2 == i2 &&\n      other.i3 == i3;\n\n  @override\n  int get hashCode =>\n      hash(<int>[i0.hashCode, i1.hashCode, i2.hashCode, i3.hashCode]);\n}\n\n/// Represents a 5-tuple or pair.\nclass Tuple5<T0, T1, T2, T3, T4> {\n  T0 i0;\n  T1 i1;\n  T2 i2;\n  T3 i3;\n  T4 i4;\n\n  Tuple5([this.i0, this.i1, this.i2, this.i3, this.i4]);\n\n  @override\n  String toString() => '[$i0, $i1, $i2, $i3, $i4]';\n\n  @override\n  bool operator ==(Object other) =>\n      other is Tuple5 &&\n      other.i0 == i0 &&\n      other.i1 == i1 &&\n      other.i2 == i2 &&\n      other.i3 == i3 &&\n      other.i4 == i4;\n\n  @override\n  int get hashCode => hash(\n      <int>[i0.hashCode, i1.hashCode, i2.hashCode, i3.hashCode, i4.hashCode]);\n}\n\n/// Represents a 6-tuple or pair.\nclass Tuple6<T0, T1, T2, T3, T4, T5> {\n  T0 i0;\n  T1 i1;\n  T2 i2;\n  T3 i3;\n  T4 i4;\n  T5 i5;\n\n  Tuple6([this.i0, this.i1, this.i2, this.i3, this.i4, this.i5]);\n\n  @override\n  String toString() => '[$i0, $i1, $i2, $i3, $i4, $i5]';\n\n  @override\n  bool operator ==(Object other) =>\n      other is Tuple6 &&\n      other.i0 == i0 &&\n      other.i1 == i1 &&\n      other.i2 == i2 &&\n      other.i3 == i3 &&\n      other.i4 == i4 &&\n      other.i5 == i5;\n\n  @override\n  int get hashCode => hash(<int>[\n        i0.hashCode,\n        i1.hashCode,\n        i2.hashCode,\n        i3.hashCode,\n        i4.hashCode,\n        i5.hashCode\n      ]);\n}\n"
  },
  {
    "path": "lib/src/utils/utils.dart",
    "content": "export 'collections.dart';\nexport 'debug.dart';\nexport 'tuple.dart';\n"
  },
  {
    "path": "pubspec.yaml",
    "content": "name: fish_redux\ndescription: Fish Redux is an assembled flutter application framework based on Redux state management.\nversion: 0.3.7\nauthor: Alibaba Xianyu Team <tino.wjf@alibaba-inc.com>\nhomepage: https://github.com/alibaba/fish-redux\n\nenvironment:\n    sdk: \">=2.2.0 <3.0.0\"\ndependencies:\n    flutter:\n        sdk: flutter\n\ndev_dependencies:\n    test: ^1.5.1\n    flutter_test:\n        sdk: flutter\n\n    test_widgets:\n        path: test/test_widgets\n\n# For information on the generic Dart part of this file, see the\n# following page: https://www.dartlang.org/tools/pub/pubspec\n\n# The following section is specific to Flutter.\nflutter:\n    # To add assets to your package, add an assets section, like this:\n    # assets:\n    #  - images/a_dot_burr.jpeg\n    #  - images/a_dot_ham.jpeg\n    #\n    # For details regarding assets in packages, see\n    # https://flutter.io/assets-and-images/#from-packages\n    #\n    # An image asset can refer to one or more resolution-specific \"variants\", see\n    # https://flutter.io/assets-and-images/#resolution-aware.\n    # To add custom fonts to your package, add a fonts section here,\n    # in this \"flutter\" section. Each entry in this list should have a\n    # \"family\" key with the font family name, and a \"fonts\" key with a\n    # list giving the asset and other descriptors for the font. For\n    # example:\n    # fonts:\n    #   - family: Schyler\n    #     fonts:\n    #       - asset: fonts/Schyler-Regular.ttf\n    #       - asset: fonts/Schyler-Italic.ttf\n    #         style: italic\n    #   - family: Trajan Pro\n    #     fonts:\n    #       - asset: fonts/TrajanPro.ttf\n    #       - asset: fonts/TrajanPro_Bold.ttf\n    #         weight: 700\n    #\n    # For details regarding fonts in packages, see\n    # https://flutter.io/custom-fonts/#from-packages\n"
  },
  {
    "path": "test/lib/all_test.dart",
    "content": "import 'package:test/test.dart';\n\nimport 'redux/redux_test.dart' as redux;\nimport 'redux_adapter/redux_adapter_test.dart' as redux_adapter;\nimport 'redux_aop/redux_aop_test.dart' as redux_aop;\nimport 'redux_component/redux_component_test.dart' as redux_component;\nimport 'redux_connector/redux_connector_test.dart' as redux_connector;\nimport 'redux_middleware/redux_middleware_test.dart' as redux_middleware;\nimport 'redux_routes/redux_routes_test.dart' as redux_routes;\nimport 'utils/utils_test.dart' as utils;\n\nvoid main() {\n  group('all_test', () {\n    redux.main();\n    redux_adapter.main();\n    redux_aop.main();\n    redux_component.main();\n    redux_connector.main();\n    redux_middleware.main();\n    redux_routes.main();\n    utils.main();\n  });\n}\n"
  },
  {
    "path": "test/lib/instrument.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\ntypedef ViewInstrument<T> = void Function(\n    T state, Dispatch dispatch, ViewService viewService);\n\nViewBuilder<T> instrumentView<T>(\n        ViewBuilder<T> builder, ViewInstrument<T> pre) =>\n    (\n      T state,\n      Dispatch dispatch,\n      ViewService viewService,\n    ) {\n      if (pre != null) {\n        pre(state, dispatch, viewService);\n      }\n\n      return builder(state, dispatch, viewService);\n    };\n\ntypedef initStateInstrumentPre<P> = void Function(P);\ntypedef initStateInstrumentSuf<T extends Cloneable<T>> = void Function(T);\n\nInitState<T, P> instrumentInitState<T extends Cloneable<T>, P>(\n        InitState<T, P> initState,\n        {initStateInstrumentPre<P> pre,\n        initStateInstrumentSuf<T> suf}) =>\n    (P params) {\n      if (pre != null) {\n        pre(params);\n      }\n      final T state = initState(params);\n\n      if (suf != null) {\n        suf(state);\n      }\n\n      return state;\n    };\n\ntypedef ReducerInstrument<T> = void Function(T state, Action action);\n\nReducer<T> instrumentReducer<T>(Reducer<T> reducer,\n        {ReducerInstrument<T> pre,\n        ReducerInstrument<T> suf,\n        ReducerInstrument<T> change}) =>\n    (T state, Action action) {\n      T newState = state;\n      if (pre != null) {\n        pre(state, action);\n      }\n\n      newState = reducer(state, action);\n\n      if (suf != null) {\n        suf(newState, action);\n      }\n\n      if (change != null && newState != state) {\n        change(newState, action);\n      }\n\n      return newState;\n    };\n\ntypedef EffectInstrument<T> = void Function(Action action, Get<T> getState);\n\nEffect<T> instrumentEffect<T>(Effect<T> effect, EffectInstrument<T> pre) =>\n    (Action action, Context<T> ctx) {\n      if (pre != null) {\n        pre(action, () => ctx.state);\n      }\n      return effect(action, ctx);\n    };\n\ntypedef MiddlewareInstrument<T> = void Function(Action action, Get<T> getState);\n\nMiddleware<T> instrumentMiddleware<T>(Middleware<T> middleware,\n        {EffectInstrument<T> pre, EffectInstrument<T> suf}) =>\n    ({\n      Dispatch dispatch,\n      Get<T> getState,\n    }) {\n      return (Dispatch next) {\n        return (Action action) {\n          if (pre != null) {\n            pre(action, getState);\n          }\n\n          middleware(dispatch: dispatch, getState: getState)(next)(action);\n\n          if (suf != null) {\n            suf(action, getState);\n          }\n        };\n      };\n    };\n\ntypedef ErrorInstrument<T> = void Function(Exception exception, Context<T> ctx);\n"
  },
  {
    "path": "test/lib/redux/redux_test.dart",
    "content": "import 'package:test/test.dart';\n\nimport 'store_test.dart' as store;\n\nvoid main() {\n  group('redux_test', () {\n    store.main();\n  });\n}\n"
  },
  {
    "path": "test/lib/redux/store_test.dart",
    "content": "import 'dart:async';\nimport 'dart:ui';\n\nimport 'package:fish_redux/fish_redux.dart';\nimport 'package:test/test.dart';\n\nimport '../instrument.dart';\nimport '../track.dart';\n\nenum ToDoAction { add, remove, done }\n\nclass Todo {\n  String id;\n  String title;\n  String desc;\n  bool isDone = false;\n\n  Todo();\n\n  factory Todo.copy(Todo toDo) {\n    return Todo()\n      ..id = toDo.id\n      ..title = toDo.title\n      ..desc = toDo.desc\n      ..isDone = toDo.isDone;\n  }\n}\n\nclass ToDoList {\n  List<Todo> list = <Todo>[];\n\n  ToDoList();\n\n  factory ToDoList.copy(ToDoList toDoState) {\n    return ToDoList()..list.addAll(toDoState.list);\n  }\n}\n\nToDoList toDoReducer(ToDoList state, Action action) {\n  final ToDoList newState = ToDoList.copy(state);\n\n  if (action.type == ToDoAction.add) {\n    newState.list.add(action.payload);\n  } else if (action.type == ToDoAction.remove) {\n    newState.list.removeWhere((Todo toDo) => toDo.id == action.payload);\n  } else if (action.type == ToDoAction.done) {\n    newState.list\n        .firstWhere((Todo toDo) => toDo.id == action.payload,\n            orElse: () => null)\n        ?.isDone = true;\n  }\n\n  return newState;\n}\n\nToDoList defaultReducer(ToDoList state, Action action) => ToDoList.copy(state);\n\nvoid main() {\n  group('store', () {\n    test('create without preloadedState', () {\n      expect(() => createStore<ToDoList>(null, null), throwsArgumentError);\n    });\n\n    test('create without reducer', () {\n      final Store<ToDoList> store = createStore<ToDoList>(ToDoList(), null);\n      expect(store, isNotNull);\n      expect(store, const TypeMatcher<Store<ToDoList>>());\n      expect(store.getState(), isNotNull);\n      expect(store.getState(), const TypeMatcher<ToDoList>());\n    });\n\n    test('create', () {\n      final Store<ToDoList> store =\n          createStore<ToDoList>(ToDoList(), toDoReducer);\n      expect(store, isNotNull);\n      expect(store, const TypeMatcher<Store<ToDoList>>());\n      expect(store.getState(), isNotNull);\n      expect(store.getState(), const TypeMatcher<ToDoList>());\n    });\n\n    test('dispatch & state', () {\n      final Track track = Track();\n      final Store<ToDoList> store = createStore<ToDoList>(\n          ToDoList(),\n          instrumentReducer(toDoReducer, pre: (ToDoList state, Action action) {\n            if (action.type == ToDoAction.add) {\n              track.append('onReduce_Add');\n            } else if (action.type == ToDoAction.done) {\n              track.append('onReduce_Done');\n            } else if (action.type == ToDoAction.remove) {\n              track.append('onReduce_Remove');\n            }\n          }));\n\n      expect(store.getState(), isNotNull);\n      expect(store.getState().list, isNotNull);\n      expect(store.getState().list, isEmpty);\n\n      track.append('dispatch_Add');\n      store.dispatch(Action(ToDoAction.add,\n          payload: Todo()\n            ..id = 'unique'\n            ..title = 'test'\n            ..desc = 'just test'));\n\n      expect(store.getState(), isNotNull);\n      expect(store.getState().list, isNotNull);\n      expect(store.getState().list, isNotEmpty);\n      expect(store.getState().list.first, isNotNull);\n      expect(store.getState().list.first.id, 'unique');\n      expect(store.getState().list.first.title, 'test');\n      expect(store.getState().list.first.desc, 'just test');\n      expect(store.getState().list.first.isDone, isFalse);\n\n      track.append('dispatch_Done');\n      store.dispatch(const Action(ToDoAction.done, payload: 'unique'));\n\n      expect(store.getState(), isNotNull);\n      expect(store.getState().list, isNotNull);\n      expect(store.getState().list, isNotEmpty);\n      expect(store.getState().list.first, isNotNull);\n      expect(store.getState().list.first.id, 'unique');\n      expect(store.getState().list.first.title, 'test');\n      expect(store.getState().list.first.desc, 'just test');\n      expect(store.getState().list.first.isDone, isTrue);\n\n      track.append('dispatch_Remove');\n      store.dispatch(const Action(ToDoAction.remove, payload: 'unique'));\n\n      expect(store.getState(), isNotNull);\n      expect(store.getState().list, isNotNull);\n      expect(store.getState().list, isEmpty);\n\n      expect(\n          track,\n          Track.tags(<String>[\n            'dispatch_Add',\n            'onReduce_Add',\n            'dispatch_Done',\n            'onReduce_Done',\n            'dispatch_Remove',\n            'onReduce_Remove'\n          ]));\n    });\n\n    test('subscribe', () {\n      final Track track = Track();\n\n      final Store<ToDoList> store = createStore<ToDoList>(\n          ToDoList(),\n          instrumentReducer(toDoReducer, pre: (ToDoList state, Action action) {\n            if (action.type == ToDoAction.add) {\n              track.append('onReduce_Add');\n            } else if (action.type == ToDoAction.done) {\n              track.append('onReduce_Done');\n            } else if (action.type == ToDoAction.remove) {\n              track.append('onReduce_Remove');\n            } else {\n              track.append('onReduce_Unkonw');\n            }\n          }));\n\n      Todo firstToDo;\n      store.subscribe(() {\n        track.append('onSubscribe');\n        firstToDo = store.getState().list.isEmpty\n            ? null\n            : Todo.copy(store.getState().list.first);\n      });\n\n      expect(firstToDo, isNull);\n\n      track.append('dispatch_Add');\n      store.dispatch(Action(ToDoAction.add,\n          payload: Todo()\n            ..id = 'unique'\n            ..title = 'test'\n            ..desc = 'just test'));\n\n      expect(firstToDo, isNotNull);\n      expect(firstToDo.id, 'unique');\n      expect(firstToDo.title, 'test');\n      expect(firstToDo.desc, 'just test');\n      expect(firstToDo.isDone, isFalse);\n\n      track.append('dispatch_Done');\n      store.dispatch(const Action(ToDoAction.done, payload: 'unique'));\n\n      expect(firstToDo, isNotNull);\n      expect(firstToDo.id, 'unique');\n      expect(firstToDo.title, 'test');\n      expect(firstToDo.desc, 'just test');\n      expect(firstToDo.isDone, isTrue);\n\n      track.append('dispatch_Remove');\n      store.dispatch(const Action(ToDoAction.remove, payload: 'unique'));\n\n      expect(firstToDo, isNull);\n\n      expect(\n          track,\n          Track.tags(<String>[\n            'dispatch_Add',\n            'onReduce_Add',\n            'onSubscribe',\n            'dispatch_Done',\n            'onReduce_Done',\n            'onSubscribe',\n            'dispatch_Remove',\n            'onReduce_Remove',\n            'onSubscribe'\n          ]));\n    });\n\n    test('unsubscribe', () {\n      final Store<ToDoList> store =\n          createStore<ToDoList>(ToDoList(), toDoReducer);\n\n      Todo firstToDo;\n      final VoidCallback unsubscribe = store.subscribe(() {\n        firstToDo = store.getState().list.isEmpty\n            ? null\n            : Todo.copy(store.getState().list.first);\n      });\n\n      expect(firstToDo, isNull);\n\n      store.dispatch(Action(ToDoAction.add,\n          payload: Todo()\n            ..id = 'unique'\n            ..title = 'test'\n            ..desc = 'just test'));\n\n      expect(firstToDo, isNotNull);\n      expect(firstToDo.id, 'unique');\n      expect(firstToDo.title, 'test');\n      expect(firstToDo.desc, 'just test');\n      expect(firstToDo.isDone, isFalse);\n\n      unsubscribe();\n\n      store.dispatch(const Action(ToDoAction.remove, payload: 'unique'));\n\n      expect(firstToDo, isNotNull);\n      expect(firstToDo.id, 'unique');\n      expect(firstToDo.title, 'test');\n      expect(firstToDo.desc, 'just test');\n      expect(firstToDo.isDone, isFalse);\n    });\n\n    test('observable', () {\n      final Track track = Track();\n      final Store<ToDoList> store = createStore<ToDoList>(\n          ToDoList(),\n          instrumentReducer(toDoReducer, pre: (ToDoList state, Action action) {\n            if (action.type == ToDoAction.add) {\n              track.append('onReduce_Add');\n            } else if (action.type == ToDoAction.done) {\n              track.append('onReduce_Done');\n            } else if (action.type == ToDoAction.remove) {\n              track.append('onReduce_Remove');\n            } else {\n              track.append('onReduce_Unkonw');\n            }\n          }));\n\n      Todo firstToDo;\n      store.observable().listen((ToDoList list) {\n        track.append('observed');\n        firstToDo = list.list.isEmpty ? null : Todo.copy(list.list.first);\n      });\n\n      expect(firstToDo, isNull);\n\n      track.append('dispatch_Add');\n      store.dispatch(Action(ToDoAction.add,\n          payload: Todo()\n            ..id = 'unique'\n            ..title = 'test'\n            ..desc = 'just test'));\n\n      expect(firstToDo, isNotNull);\n      expect(firstToDo.id, 'unique');\n      expect(firstToDo.title, 'test');\n      expect(firstToDo.desc, 'just test');\n      expect(firstToDo.isDone, isFalse);\n\n      track.append('dispatch_Done');\n      store.dispatch(const Action(ToDoAction.done, payload: 'unique'));\n\n      expect(firstToDo, isNotNull);\n      expect(firstToDo.id, 'unique');\n      expect(firstToDo.title, 'test');\n      expect(firstToDo.desc, 'just test');\n      expect(firstToDo.isDone, isTrue);\n\n      track.append('dispatch_Remove');\n      store.dispatch(const Action(ToDoAction.remove, payload: 'unique'));\n\n      expect(firstToDo, isNull);\n      expect(\n          track,\n          Track.tags(<String>[\n            'dispatch_Add',\n            'onReduce_Add',\n            'observed',\n            'dispatch_Done',\n            'onReduce_Done',\n            'observed',\n            'dispatch_Remove',\n            'onReduce_Remove',\n            'observed'\n          ]));\n    });\n\n    test('cancel observable', () {\n      final Store<ToDoList> store =\n          createStore<ToDoList>(ToDoList(), toDoReducer);\n\n      Todo firstToDo;\n      final StreamSubscription<ToDoList> subscription =\n          store.observable().listen((ToDoList list) {\n        firstToDo = list.list.isEmpty ? null : Todo.copy(list.list.first);\n      });\n\n      expect(firstToDo, isNull);\n\n      store.dispatch(Action(ToDoAction.add,\n          payload: Todo()\n            ..id = 'unique'\n            ..title = 'test'\n            ..desc = 'just test'));\n\n      expect(firstToDo, isNotNull);\n      expect(firstToDo.id, 'unique');\n      expect(firstToDo.title, 'test');\n      expect(firstToDo.desc, 'just test');\n      expect(firstToDo.isDone, isFalse);\n\n      subscription.cancel();\n\n      store.dispatch(const Action(ToDoAction.remove, payload: 'unique'));\n\n      expect(firstToDo, isNotNull);\n      expect(firstToDo.id, 'unique');\n      expect(firstToDo.title, 'test');\n      expect(firstToDo.desc, 'just test');\n      expect(firstToDo.isDone, isFalse);\n    });\n\n    test('replaceReducer', () {\n      final Store<ToDoList> store =\n          createStore<ToDoList>(ToDoList(), toDoReducer);\n\n      expect(store.getState(), isNotNull);\n      expect(store.getState().list, isNotNull);\n      expect(store.getState().list, isEmpty);\n\n      store.dispatch(Action(ToDoAction.add,\n          payload: Todo()\n            ..id = 'unique'\n            ..title = 'test'\n            ..desc = 'just test'));\n\n      expect(store.getState(), isNotNull);\n      expect(store.getState().list, isNotNull);\n      expect(store.getState().list, isNotEmpty);\n      expect(store.getState().list.first, isNotNull);\n      expect(store.getState().list.first.id, 'unique');\n      expect(store.getState().list.first.title, 'test');\n      expect(store.getState().list.first.desc, 'just test');\n      expect(store.getState().list.first.isDone, isFalse);\n\n      store.replaceReducer(defaultReducer);\n      store.dispatch(const Action(ToDoAction.remove, payload: 'unique'));\n\n      expect(store.getState(), isNotNull);\n      expect(store.getState().list, isNotNull);\n      expect(store.getState().list, isNotEmpty);\n      expect(store.getState().list.first, isNotNull);\n      expect(store.getState().list.first.id, 'unique');\n      expect(store.getState().list.first.title, 'test');\n      expect(store.getState().list.first.desc, 'just test');\n      expect(store.getState().list.first.isDone, isFalse);\n\n      store.replaceReducer(toDoReducer);\n      store.dispatch(const Action(ToDoAction.remove, payload: 'unique'));\n\n      expect(store.getState(), isNotNull);\n      expect(store.getState().list, isNotNull);\n      expect(store.getState().list, isEmpty);\n    });\n\n    test('applyMiddleware', () {\n      Object lastAction;\n      ToDoList lastState;\n\n      final Track track = Track();\n\n      final Middleware<ToDoList> toDoMiddleware = (\n              {Dispatch dispatch, Get<ToDoList> getState}) =>\n          (Dispatch next) => (Action action) {\n                lastAction = action.type;\n                lastState = ToDoList.copy(getState());\n                next(action);\n              };\n\n      final Store<ToDoList> store = createStore<ToDoList>(\n          ToDoList(),\n          instrumentReducer(toDoReducer, pre: (ToDoList state, Action action) {\n            if (action.type == ToDoAction.add) {\n              track.append('onReduce_Add');\n            } else if (action.type == ToDoAction.done) {\n              track.append('onReduce_Done');\n            } else if (action.type == ToDoAction.remove) {\n              track.append('onReduce_Remove');\n            } else {\n              track.append('onReduce_Unkonw');\n            }\n          }),\n          applyMiddleware(<Middleware<ToDoList>>[\n            instrumentMiddleware(toDoMiddleware,\n                pre: (Action action, Get<ToDoList> getReducer) {\n              if (action.type == ToDoAction.add) {\n                track.append('onMiddleware_Add');\n              } else if (action.type == ToDoAction.done) {\n                track.append('onMiddleware_Done');\n              } else if (action.type == ToDoAction.remove) {\n                track.append('onMiddleware_Remove');\n              } else {\n                track.append('onMiddleware_Unkonw');\n              }\n            })\n          ]));\n\n      store.subscribe(() {\n        track.append('onSubscribe');\n      });\n\n      expect(store, isNotNull);\n      expect(store, const TypeMatcher<Store<ToDoList>>());\n      expect(store.getState(), isNotNull);\n      expect(store.getState(), const TypeMatcher<ToDoList>());\n\n      expect(store.getState(), isNotNull);\n      expect(store.getState().list, isNotNull);\n      expect(store.getState().list, isEmpty);\n\n      expect(lastAction, isNull);\n      expect(lastState, isNull);\n\n      track.append('dispatch_Add');\n      store.dispatch(Action(ToDoAction.add,\n          payload: Todo()\n            ..id = 'unique'\n            ..title = 'test'\n            ..desc = 'just test'));\n\n      expect(lastAction, ToDoAction.add);\n      expect(lastState, isNotNull);\n      expect(lastState.list, isNotNull);\n      expect(lastState.list, isEmpty);\n\n      track.append('dispatch_Done');\n      store.dispatch(const Action(ToDoAction.done, payload: 'unique'));\n\n      expect(lastAction, ToDoAction.done);\n      expect(lastState, isNotNull);\n      expect(lastState.list, isNotNull);\n      expect(lastState.list, isNotEmpty);\n      expect(lastState.list.first, isNotNull);\n      expect(lastState.list.first.id, 'unique');\n      expect(lastState.list.first.title, 'test');\n      expect(lastState.list.first.desc, 'just test');\n      expect(lastState.list.first.isDone, isTrue);\n\n      track.append('dispatch_Remove');\n      store.dispatch(const Action(ToDoAction.remove, payload: 'unique'));\n\n      expect(lastAction, ToDoAction.remove);\n      expect(lastState, isNotNull);\n      expect(lastState.list, isNotNull);\n      expect(lastState.list, isNotEmpty);\n      expect(lastState.list.first, isNotNull);\n      expect(lastState.list.first.id, 'unique');\n      expect(lastState.list.first.title, 'test');\n      expect(lastState.list.first.desc, 'just test');\n      expect(lastState.list.first.isDone, isTrue);\n\n      expect(\n          track,\n          Track.tags(<String>[\n            'dispatch_Add',\n            'onMiddleware_Add',\n            'onReduce_Add',\n            'onSubscribe',\n            'dispatch_Done',\n            'onMiddleware_Done',\n            'onReduce_Done',\n            'onSubscribe',\n            'dispatch_Remove',\n            'onMiddleware_Remove',\n            'onReduce_Remove',\n            'onSubscribe'\n          ]));\n    });\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_adapter/adapter_test.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:test_widgets/adapter/action.dart';\nimport 'package:test_widgets/adapter/adapter.dart';\nimport 'package:test_widgets/adapter/page.dart';\nimport 'package:test_widgets/adapter/state.dart';\nimport 'package:test_widgets/test_base.dart';\n\nimport '../instrument.dart';\nimport '../track.dart';\n\nvoid main() {\n  group('adapter', () {\n    test('create', () {\n      TestPage<ToDoList, Map> page = TestPage<ToDoList, Map>(\n          initState: initState,\n          view: pageView,\n          dependencies: Dependencies<ToDoList>(\n              adapter: NoneConn<ToDoList>() +\n                  TestAdapter<ToDoList>(\n                      adapter: toDoListAdapter,\n                      reducer: toDoListReducer,\n                      effect: toDoListEffect)));\n      expect(page, isNotNull);\n\n      final Widget pageWidget = page.buildPage(pageInitParams);\n      expect(pageWidget, isNotNull);\n    });\n\n    testWidgets('build', (WidgetTester tester) async {\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: pageView,\n              dependencies: Dependencies<ToDoList>(\n                  adapter: NoneConn<ToDoList>() +\n                      TestAdapter<ToDoList>(\n                          adapter: toDoListAdapter,\n                          reducer: toDoListReducer,\n                          effect: toDoListEffect)))\n          .buildPage(pageInitParams)));\n\n      expect(find.byKey(const ValueKey<String>('mark-0')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-0')), findsOneWidget);\n      expect(find.text('desc-0'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-0')), findsOneWidget);\n      expect(find.text('title-0'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-1')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-1')), findsOneWidget);\n      expect(find.text('desc-1'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-1')), findsOneWidget);\n      expect(find.text('title-1'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-2')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-2')), findsOneWidget);\n      expect(find.text('desc-2'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-2')), findsOneWidget);\n      expect(find.text('title-2'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-3')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-3')), findsOneWidget);\n      expect(find.text('desc-3'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-3')), findsOneWidget);\n      expect(find.text('title-3'), findsOneWidget);\n\n      expect(find.text('mark\\ndone'), findsNWidgets(3));\n      expect(find.text('done'), findsOneWidget);\n    });\n\n    testWidgets('reducer', (WidgetTester tester) async {\n      Track track = Track();\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('build', state.clone());\n              }),\n              dependencies: Dependencies<ToDoList>(\n                  adapter: NoneConn<ToDoList>() +\n                      TestAdapter<ToDoList>(\n                          adapter: toDoListAdapter,\n                          reducer: instrumentReducer<ToDoList>(toDoListReducer,\n                              suf: (ToDoList state, Action action) {\n                            track.append('onReduce', state.clone());\n                          }),\n                          effect: toDoListEffect)))\n          .buildPage(pageInitParams)));\n\n      await tester.tap(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump();\n\n      expect(find.text('mark\\ndone'), findsNWidgets(2));\n      expect(find.text('done'), findsNWidgets(2));\n\n      await tester.tap(find.byKey(const ValueKey<String>('mark-1')));\n      await tester.pump();\n\n      expect(find.text('mark\\ndone'), findsNWidgets(1));\n      expect(find.text('done'), findsNWidgets(3));\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-2')));\n      await tester.pump();\n\n      expect(find.byKey(const ValueKey<String>('remove-2')), findsNothing);\n      expect(find.text('desc-2'), findsNothing);\n      expect(find.text('title-2'), findsNothing);\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-3')));\n      await tester.pump();\n\n      expect(find.byKey(const ValueKey<String>('remove-3')), findsNothing);\n      expect(find.text('desc-3'), findsNothing);\n      expect(find.text('title-3'), findsNothing);\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('build', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState,\n                  Action(ToDoListAction.markDone,\n                      payload: mockState.list.firstWhere((i) => i.id == '0')));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState,\n                  Action(ToDoListAction.markDone,\n                      payload: mockState.list.firstWhere((i) => i.id == '1')));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState,\n                  Action(ToDoListAction.remove,\n                      payload: mockState.list.firstWhere((i) => i.id == '2')));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState,\n                  Action(ToDoListAction.remove,\n                      payload: mockState.list.firstWhere((i) => i.id == '3')));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n          ]));\n    });\n\n    testWidgets('effect', (WidgetTester tester) async {\n      Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('build', state.clone());\n              }),\n              dependencies: Dependencies<ToDoList>(\n                  adapter: NoneConn<ToDoList>() +\n                      TestAdapter<ToDoList>(\n                          adapter: toDoListAdapter,\n                          reducer: instrumentReducer<ToDoList>(toDoListReducer,\n                              change: (ToDoList state, Action action) {\n                            track.append('onReduce', state.clone());\n                          }),\n                          effect: instrumentEffect(toDoListEffect,\n                              (Action action, Get<ToDoList> getState) {\n                            if (action.type == ToDoListAction.onAdd) {\n                              track.append('onAdd', getState().clone());\n                            } else if (action.type == ToDoListAction.onEdit) {\n                              track.append('onEdit', getState().clone());\n                            }\n                          }))))\n          .buildPage(pageInitParams)));\n\n      await tester.longPress(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump();\n\n      expect(find.text('title-mock', skipOffstage: false), findsNWidgets(1));\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(1));\n\n      await tester.longPress(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump();\n\n      expect(find.text('title-mock', skipOffstage: false), findsNWidgets(2));\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(2));\n\n      await tester.tap(find.byKey(const ValueKey<String>('edit-0')));\n      await tester.pump();\n\n      expect(find.text('title-0', skipOffstage: false), findsOneWidget);\n      expect(find.text('desc-0-effect', skipOffstage: false), findsOneWidget);\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n\n      expect(\n          track,\n          Track.pins([\n            Pin('build', mockState.clone()),\n            Pin('onAdd', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onAdd', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onEdit', mockState.clone()),\n            Pin('onReduce', () {\n              Todo toDo = mockState.list[0].clone();\n              toDo.desc = '${toDo.desc}-effect';\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.edit, payload: toDo));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n          ]));\n    });\n\n    testWidgets('effectAsync', (WidgetTester tester) async {\n      Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('build', state.clone());\n              }),\n              dependencies: Dependencies<ToDoList>(\n                  adapter: NoneConn<ToDoList>() +\n                      TestAdapter<ToDoList>(\n                          adapter: toDoListAdapter,\n                          reducer: instrumentReducer<ToDoList>(toDoListReducer,\n                              change: (ToDoList state, Action action) {\n                            track.append('onReduce', state.clone());\n                          }),\n                          effect: instrumentEffect(toDoListEffectAsync,\n                              (Action action, Get<ToDoList> getState) {\n                            if (action.type == ToDoListAction.onAdd) {\n                              track.append('onAdd', getState().clone());\n                            } else if (action.type == ToDoListAction.onEdit) {\n                              track.append('onEdit', getState().clone());\n                            }\n                          }))))\n          .buildPage(pageInitParams)));\n\n      await tester.longPress(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump(Duration(seconds: 3));\n\n      expect(find.text('title-mock', skipOffstage: false), findsNWidgets(1));\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(1));\n\n      await tester.longPress(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump(Duration(seconds: 3));\n\n      expect(find.text('title-mock', skipOffstage: false), findsNWidgets(2));\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(2));\n\n      await tester.tap(find.byKey(const ValueKey<String>('edit-0')));\n      await tester.pump(Duration(seconds: 3));\n\n      expect(find.text('title-0', skipOffstage: false), findsOneWidget);\n      expect(find.text('desc-0-effect', skipOffstage: false), findsOneWidget);\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n\n      expect(\n          track,\n          Track.pins([\n            Pin('build', mockState.clone()),\n            Pin('onAdd', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onAdd', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onEdit', mockState.clone()),\n            Pin('onReduce', () {\n              Todo toDo = mockState.list[0].clone();\n              toDo.desc = '${toDo.desc}-effect';\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.edit, payload: toDo));\n              print(mockState);\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n          ]));\n    });\n    testWidgets('effect', (WidgetTester tester) async {\n      Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('build', state.clone());\n              }),\n              dependencies: Dependencies<ToDoList>(\n                  adapter: NoneConn<ToDoList>() +\n                      TestAdapter<ToDoList>(\n                          adapter: toDoListAdapter,\n                          reducer: instrumentReducer<ToDoList>(toDoListReducer,\n                              change: (ToDoList state, Action action) {\n                            track.append('onReduce', state.clone());\n                          }),\n                          effect: (Action action, Context<ToDoList> ctx) =>\n                              instrumentEffect(toDoListEffectAsync,\n                                  (Action action, Get<ToDoList> getState) {\n                                if (action.type == ToDoListAction.onAdd) {\n                                  track.append('onAdd', getState().clone());\n                                } else if (action.type ==\n                                    ToDoListAction.onEdit) {\n                                  track.append('onEdit', getState().clone());\n                                }\n                              })(action, ctx))))\n          .buildPage(pageInitParams)));\n\n      await tester.longPress(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump(Duration(seconds: 3));\n\n      expect(find.text('title-mock', skipOffstage: false), findsNWidgets(1));\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(1));\n\n      await tester.longPress(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump(Duration(seconds: 3));\n\n      expect(find.text('title-mock', skipOffstage: false), findsNWidgets(2));\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(2));\n\n      await tester.tap(find.byKey(const ValueKey<String>('edit-0')));\n      await tester.pump(Duration(seconds: 3));\n\n      expect(find.text('title-0', skipOffstage: false), findsOneWidget);\n      expect(find.text('desc-0-effect', skipOffstage: false), findsOneWidget);\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n\n      expect(\n          track,\n          Track.pins([\n            Pin('build', mockState.clone()),\n            Pin('onAdd', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onAdd', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onEdit', mockState.clone()),\n            Pin('onReduce', () {\n              Todo toDo = mockState.list[0].clone();\n              toDo.desc = '${toDo.desc}-effect';\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.edit, payload: toDo));\n              print(mockState);\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n          ]));\n    });\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_adapter/dynamic_adapter_test.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:test_widgets/dynamic_flow_adapter/action.dart';\nimport 'package:test_widgets/dynamic_flow_adapter/component.dart';\nimport 'package:test_widgets/dynamic_flow_adapter/dynamic_flow_adapter.dart';\nimport 'package:test_widgets/dynamic_flow_adapter/page.dart';\nimport 'package:test_widgets/dynamic_flow_adapter/state.dart';\nimport 'package:test_widgets/test_base.dart';\n\nimport '../instrument.dart';\nimport '../track.dart';\n\nclass ToDoComponentInstrument extends TestComponent<Todo> {\n  ToDoComponentInstrument(final Track track)\n      : super(\n          view: instrumentView<Todo>(toDoView,\n              (Todo state, Dispatch dispatch, ViewService viewService) {\n            track.append('toDo-build', state.clone());\n          }),\n          reducer: instrumentReducer<Todo>(toDoReducer,\n              change: (Todo state, Action action) {\n            track.append('toDo-onReduce', state.clone());\n          }),\n          effect: instrumentEffect<Todo>(toDoEffect,\n              (Action action, Get<Todo> getState) {\n            if (action.type == ToDoAction.onEdit) {\n              track.append('toDo-onEdit', getState().clone());\n            } else if (action.type == ToDoAction.broadcast) {\n              track.append('toDo-onToDoBroadcast', getState().clone());\n            } else if (action.type == ToDoListAction.broadcast) {\n              track.append('toDo-onPageBroadcast', getState().clone());\n            }\n          }),\n          shouldUpdate: shouldUpdate,\n          key: (Todo toDo) => GlobalObjectKey(toDo.id),\n        );\n}\n\nclass ToDoComponentNoReducer extends TestComponent<Todo> {\n  ToDoComponentNoReducer(final Track track)\n      : super(\n          view: instrumentView<Todo>(toDoView,\n              (Todo state, Dispatch dispatch, ViewService viewService) {\n            track.append('toDo-build', state.clone());\n          }),\n          effect: instrumentEffect<Todo>(toDoEffect,\n              (Action action, Get<Todo> getState) {\n            if (action.type == ToDoAction.onEdit) {\n              track.append('toDo-onEdit', getState().clone());\n            } else if (action.type == ToDoAction.broadcast) {\n              track.append('toDo-onToDoBroadcast', getState().clone());\n            } else if (action.type == ToDoListAction.broadcast) {\n              track.append('toDo-onPageBroadcast', getState().clone());\n            }\n          }),\n          shouldUpdate: shouldUpdate,\n          key: (Todo toDo) => GlobalObjectKey(toDo.id),\n        );\n}\n\nDependencies<ToDoList> toDoListDependencies(final Track track,\n        {bool noReducer = false}) =>\n    Dependencies<ToDoList>(\n        adapter: NoneConn<ToDoList>() +\n            TestDynamicFlowAdapter<ToDoList>(\n                pool: <String, AbstractLogic<Todo>>{\n                  'toDo': ToDoComponentInstrument(track),\n                  'toDoNoReducer': ToDoComponentNoReducer(track),\n                },\n                connector: ConnOp<ToDoList, List<ItemBean>>(\n                    get: (ToDoList toDoList) => toDoList.list\n                        .map<ItemBean>((Todo toDo) => noReducer\n                            ? ItemBean('toDoNoReducer', toDo)\n                            : ItemBean('toDo', toDo))\n                        .toList(),\n                    set: (ToDoList toDoList, List<ItemBean> beans) {\n                      toDoList.list.clear();\n                      toDoList.list.addAll(beans\n                          .map<Todo>((ItemBean bean) => bean.data)\n                          .toList());\n                    }),\n                reducer: instrumentReducer<ToDoList>(toDoListReducer,\n                    change: (ToDoList state, Action action) {\n                  track.append('adapter-onReduce', state.clone());\n                }),\n                effect: instrumentEffect<ToDoList>(toDoListEffect,\n                    (Action action, Get<ToDoList> getState) {\n                  if (action.type == ToDoListAction.onAdd) {\n                    track.append('adapter-onAdd', getState().clone());\n                  } else if (action.type == ToDoAction.broadcast) {\n                    track.append('adapter-onToDoBroadcast', getState().clone());\n                  } else if (action.type == ToDoListAction.broadcast) {\n                    track.append('adapter-onPageBroadcast', getState().clone());\n                  }\n                })));\n\nvoid main() {\n  group('dynamic_flow_adapter', () {\n    test('create', () {\n      final Track track = Track();\n      final TestComponent<Todo> component = ToDoComponentInstrument(track);\n      expect(component, isNotNull);\n\n      Widget page = TestPage<ToDoList, Map>(\n              initState: initState,\n              view: pageView,\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams);\n      expect(page, isNotNull);\n    });\n\n    testWidgets('build', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              reducer: toDoListReducer,\n              effect: toDoListEffect,\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      expect(find.byKey(const ValueKey<String>('Add')), findsOneWidget);\n      expect(find.text('Add'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-0')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-0')), findsOneWidget);\n      expect(find.text('desc-0'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-0')), findsOneWidget);\n      expect(find.text('title-0'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-1')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-1')), findsOneWidget);\n      expect(find.text('desc-1'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-1')), findsOneWidget);\n      expect(find.text('title-1'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-2')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-2')), findsOneWidget);\n      expect(find.text('desc-2'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-2')), findsOneWidget);\n      expect(find.text('title-2'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-3')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-3')), findsOneWidget);\n      expect(find.text('desc-3'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-3')), findsOneWidget);\n      expect(find.text('title-3'), findsOneWidget);\n\n      expect(find.text('mark\\ndone'), findsNWidgets(3));\n      expect(find.text('done'), findsOneWidget);\n\n      expect(track.countOfTag('page-build'), 1);\n      expect(track.countOfTag('toDo-build'), 4);\n    });\n\n    testWidgets('build-noReducer', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              reducer: toDoListReducer,\n              effect: toDoListEffect,\n              dependencies: toDoListDependencies(track, noReducer: true))\n          .buildPage(pageInitParams)));\n\n      expect(find.byKey(const ValueKey<String>('Add')), findsOneWidget);\n      expect(find.text('Add'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-0')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-0')), findsOneWidget);\n      expect(find.text('desc-0'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-0')), findsOneWidget);\n      expect(find.text('title-0'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-1')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-1')), findsOneWidget);\n      expect(find.text('desc-1'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-1')), findsOneWidget);\n      expect(find.text('title-1'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-2')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-2')), findsOneWidget);\n      expect(find.text('desc-2'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-2')), findsOneWidget);\n      expect(find.text('title-2'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-3')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-3')), findsOneWidget);\n      expect(find.text('desc-3'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-3')), findsOneWidget);\n      expect(find.text('title-3'), findsOneWidget);\n\n      expect(find.text('mark\\ndone'), findsNWidgets(3));\n      expect(find.text('done'), findsOneWidget);\n\n      expect(track.countOfTag('page-build'), 1);\n      expect(track.countOfTag('toDo-build'), 4);\n    });\n\n    testWidgets('reducer', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              effect: pageEffect,\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      await tester.tap(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump();\n\n      expect(find.text('mark\\ndone'), findsNWidgets(2));\n      expect(find.text('done'), findsNWidgets(2));\n\n      await tester.tap(find.byKey(const ValueKey<String>('mark-1')));\n      await tester.pump();\n\n      expect(find.text('mark\\ndone'), findsNWidgets(1));\n      expect(find.text('done'), findsNWidgets(3));\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-2')));\n      await tester.pump();\n\n      expect(find.text('title-2'), findsNothing);\n      expect(find.text('desc-2'), findsNothing);\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-3')));\n      await tester.pump();\n\n      expect(find.text('title-3'), findsNothing);\n      expect(find.text('desc-3'), findsNothing);\n\n      print(track);\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', mockState.list[0].clone()),\n            Pin('toDo-build', mockState.list[1].clone()),\n            Pin('toDo-build', mockState.list[2].clone()),\n            Pin('toDo-build', mockState.list[3].clone()),\n            Pin('toDo-onReduce', () {\n              mockState.list[0] = toDoReducer(mockState.list[0],\n                  Action(ToDoAction.markDone, payload: mockState.list[0]));\n              return mockState.list[0].clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', mockState.list[0].clone()),\n            Pin('toDo-onReduce', () {\n              mockState.list[1] = toDoReducer(mockState.list[1],\n                  Action(ToDoAction.markDone, payload: mockState.list[1]));\n              return mockState.list[1].clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', mockState.list[1].clone()),\n            Pin('adapter-onReduce', () {\n              mockState = toDoListReducer(mockState,\n                  Action(ToDoListAction.remove, payload: mockState.list[2]));\n              return mockState.clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('adapter-onReduce', () {\n              mockState = toDoListReducer(mockState,\n                  Action(ToDoListAction.remove, payload: mockState.list[2]));\n              return mockState.clone();\n            }),\n            Pin('page-build', mockState.clone()),\n          ]));\n    });\n\n    testWidgets('effect', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              effect: pageEffect,\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      await tester.tap(find.byKey(const ValueKey<String>('edit-0')));\n      await tester.pump();\n\n      expect(find.text('desc-0-effect'), findsNWidgets(1));\n\n      await tester.tap(find.byKey(const ValueKey<String>('edit-1')));\n      await tester.pump();\n\n      expect(find.text('desc-1-effect'), findsNWidgets(1));\n\n      await tester.tap(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump();\n\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(1));\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', mockState.list[0].clone()),\n            Pin('toDo-build', mockState.list[1].clone()),\n            Pin('toDo-build', mockState.list[2].clone()),\n            Pin('toDo-build', mockState.list[3].clone()),\n            Pin('toDo-onEdit', mockState.list[0].clone()),\n            Pin('toDo-onReduce', () {\n              final Todo toDo = mockState.list[0].clone();\n              toDo.desc = '${toDo.desc}-effect';\n              mockState.list[0] =\n                  toDoReducer(toDo, Action(ToDoAction.edit, payload: toDo));\n              return mockState.list[0].clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', mockState.list[0].clone()),\n            Pin('toDo-onEdit', mockState.list[1].clone()),\n            Pin('toDo-onReduce', () {\n              final Todo toDo = mockState.list[1].clone();\n              toDo.desc = '${toDo.desc}-effect';\n              mockState.list[1] =\n                  toDoReducer(toDo, Action(ToDoAction.edit, payload: toDo));\n              return mockState.list[1].clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', mockState.list[1].clone()),\n            Pin('adapter-onAdd', mockState.clone()),\n            Pin('adapter-onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', Todo.mock()),\n          ]));\n    });\n\n    testWidgets('effect-noReducer', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              effect: pageEffect,\n              dependencies: toDoListDependencies(track, noReducer: true))\n          .buildPage(pageInitParams)));\n\n      await tester.tap(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump();\n\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(1));\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', mockState.list[0].clone()),\n            Pin('toDo-build', mockState.list[1].clone()),\n            Pin('toDo-build', mockState.list[2].clone()),\n            Pin('toDo-build', mockState.list[3].clone()),\n            Pin('adapter-onAdd', mockState.clone()),\n            Pin('adapter-onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', Todo.mock()),\n          ]));\n    });\n\n    testWidgets('broadcast', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              effect: pageEffect,\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump(Duration(seconds: 1));\n\n      print(track);\n\n      expect(track.countOfTag('toDo-onToDoBroadcast'), 4);\n      expect(track.countOfTag('adapter-onToDoBroadcast'), 1);\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump(Duration(seconds: 1));\n\n      expect(track.countOfTag('toDo-onToDoBroadcast'), 4);\n      expect(track.countOfTag('adapter-onToDoBroadcast'), 1);\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-1')));\n      await tester.pump();\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump(Duration(seconds: 1));\n\n      expect(track.countOfTag('toDo-onToDoBroadcast'), 3);\n      expect(track.countOfTag('adapter-onToDoBroadcast'), 1);\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-2')));\n      await tester.pump();\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump(Duration(seconds: 1));\n\n      expect(track.countOfTag('toDo-onToDoBroadcast'), 2);\n      expect(track.countOfTag('adapter-onToDoBroadcast'), 1);\n\n      await tester.tap(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump();\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump(Duration(seconds: 1));\n\n      expect(track.countOfTag('toDo-onToDoBroadcast'), 3);\n      expect(track.countOfTag('adapter-onToDoBroadcast'), 1);\n    });\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_adapter/redux_adapter_test.dart",
    "content": "import 'package:test/test.dart';\n\nimport 'adapter_test.dart' as adapter;\nimport 'dynamic_adapter_test.dart' as dynamic_flow_dapter;\nimport 'source_adapter_test.dart' as source_flow_dapter;\nimport 'static_flow_adapter_test.dart' as static_flow_adapter;\n\nvoid main() {\n  group('redux_adapter_test', () {\n    adapter.main();\n    dynamic_flow_dapter.main();\n    source_flow_dapter.main();\n    static_flow_adapter.main();\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_adapter/source_adapter_test.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:test_widgets/source_flow_adapter/action.dart';\nimport 'package:test_widgets/source_flow_adapter/component.dart';\nimport 'package:test_widgets/source_flow_adapter/source_flow_adapter.dart';\nimport 'package:test_widgets/source_flow_adapter/page.dart';\nimport 'package:test_widgets/source_flow_adapter/state.dart';\nimport 'package:test_widgets/test_base.dart';\n\nimport '../instrument.dart';\nimport '../track.dart';\n\nclass ToDoComponentInstrument extends TestComponent<Todo> {\n  ToDoComponentInstrument(final Track track)\n      : super(\n          view: instrumentView<Todo>(toDoView,\n              (Todo state, Dispatch dispatch, ViewService viewService) {\n            track.append('toDo-build', state.clone());\n          }),\n          reducer: instrumentReducer<Todo>(toDoReducer,\n              change: (Todo state, Action action) {\n            track.append('toDo-onReduce', state.clone());\n          }),\n          effect: instrumentEffect<Todo>(toDoEffect,\n              (Action action, Get<Todo> getState) {\n            if (action.type == ToDoAction.onEdit) {\n              track.append('toDo-onEdit', getState().clone());\n            } else if (action.type == ToDoAction.broadcast) {\n              track.append('toDo-onToDoBroadcast', getState().clone());\n            } else if (action.type == ToDoListAction.broadcast) {\n              track.append('toDo-onPageBroadcast', getState().clone());\n            }\n          }),\n          shouldUpdate: shouldUpdate,\n          key: (Todo toDo) => GlobalObjectKey(toDo.id),\n        );\n}\n\nclass ToDoComponentNoReducer extends TestComponent<Todo> {\n  ToDoComponentNoReducer(final Track track)\n      : super(\n          view: instrumentView<Todo>(toDoView,\n              (Todo state, Dispatch dispatch, ViewService viewService) {\n            track.append('toDo-build', state.clone());\n          }),\n          effect: instrumentEffect<Todo>(toDoEffect,\n              (Action action, Get<Todo> getState) {\n            if (action.type == ToDoAction.onEdit) {\n              track.append('toDo-onEdit', getState().clone());\n            } else if (action.type == ToDoAction.broadcast) {\n              track.append('toDo-onToDoBroadcast', getState().clone());\n            } else if (action.type == ToDoListAction.broadcast) {\n              track.append('toDo-onPageBroadcast', getState().clone());\n            }\n          }),\n          shouldUpdate: shouldUpdate,\n          key: (Todo toDo) => GlobalObjectKey(toDo.id),\n        );\n}\n\nDependencies<ToDoList> toDoListDependencies(final Track track,\n        {bool noReducer = false}) =>\n    Dependencies<ToDoList>(\n        adapter: NoneConn<ToDoList>() +\n            TestSourceFlowAdapter<ToDoList>(\n                pool: <String, AbstractLogic<Todo>>{\n                  'toDo': ToDoComponentInstrument(track),\n                  'toDoNoReducer': ToDoComponentNoReducer(track),\n                  'item': noReducer\n                      ? ToDoComponentNoReducer(track)\n                      : ToDoComponentInstrument(track),\n                },\n                reducer: instrumentReducer<ToDoList>(toDoListReducer,\n                    change: (ToDoList state, Action action) {\n                  track.append('adapter-onReduce', state.clone());\n                }),\n                effect: instrumentEffect<ToDoList>(toDoListEffect,\n                    (Action action, Get<ToDoList> getState) {\n                  if (action.type == ToDoListAction.onAdd) {\n                    track.append('adapter-onAdd', getState().clone());\n                  } else if (action.type == ToDoAction.broadcast) {\n                    track.append('adapter-onToDoBroadcast', getState().clone());\n                  } else if (action.type == ToDoListAction.broadcast) {\n                    track.append('adapter-onPageBroadcast', getState().clone());\n                  }\n                })));\n\nvoid main() {\n  group('source_flow_adapter', () {\n    test('create', () {\n      final Track track = Track();\n      final TestComponent<Todo> component = ToDoComponentInstrument(track);\n      expect(component, isNotNull);\n\n      Widget page = TestPage<ToDoList, Map>(\n              initState: initState,\n              view: pageView,\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams);\n      expect(page, isNotNull);\n    });\n\n    testWidgets('build', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              reducer: toDoListReducer,\n              effect: toDoListEffect,\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      expect(find.byKey(const ValueKey<String>('Add')), findsOneWidget);\n      expect(find.text('Add'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-0')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-0')), findsOneWidget);\n      expect(find.text('desc-0'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-0')), findsOneWidget);\n      expect(find.text('title-0'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-1')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-1')), findsOneWidget);\n      expect(find.text('desc-1'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-1')), findsOneWidget);\n      expect(find.text('title-1'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-2')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-2')), findsOneWidget);\n      expect(find.text('desc-2'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-2')), findsOneWidget);\n      expect(find.text('title-2'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-3')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-3')), findsOneWidget);\n      expect(find.text('desc-3'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-3')), findsOneWidget);\n      expect(find.text('title-3'), findsOneWidget);\n\n      expect(find.text('mark\\ndone'), findsNWidgets(3));\n      expect(find.text('done'), findsOneWidget);\n\n      expect(track.countOfTag('page-build'), 1);\n      expect(track.countOfTag('toDo-build'), 4);\n    });\n\n    testWidgets('build-noReducer', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              reducer: toDoListReducer,\n              effect: toDoListEffect,\n              dependencies: toDoListDependencies(track, noReducer: true))\n          .buildPage(pageInitParams)));\n\n      expect(find.byKey(const ValueKey<String>('Add')), findsOneWidget);\n      expect(find.text('Add'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-0')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-0')), findsOneWidget);\n      expect(find.text('desc-0'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-0')), findsOneWidget);\n      expect(find.text('title-0'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-1')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-1')), findsOneWidget);\n      expect(find.text('desc-1'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-1')), findsOneWidget);\n      expect(find.text('title-1'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-2')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-2')), findsOneWidget);\n      expect(find.text('desc-2'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-2')), findsOneWidget);\n      expect(find.text('title-2'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-3')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-3')), findsOneWidget);\n      expect(find.text('desc-3'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-3')), findsOneWidget);\n      expect(find.text('title-3'), findsOneWidget);\n\n      expect(find.text('mark\\ndone'), findsNWidgets(3));\n      expect(find.text('done'), findsOneWidget);\n\n      expect(track.countOfTag('page-build'), 1);\n      expect(track.countOfTag('toDo-build'), 4);\n    });\n\n    testWidgets('reducer', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              effect: pageEffect,\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      await tester.tap(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump();\n\n      expect(find.text('mark\\ndone'), findsNWidgets(2));\n      expect(find.text('done'), findsNWidgets(2));\n\n      await tester.tap(find.byKey(const ValueKey<String>('mark-1')));\n      await tester.pump();\n\n      expect(find.text('mark\\ndone'), findsNWidgets(1));\n      expect(find.text('done'), findsNWidgets(3));\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-2')));\n      await tester.pump();\n\n      expect(find.text('title-2'), findsNothing);\n      expect(find.text('desc-2'), findsNothing);\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-3')));\n      await tester.pump();\n\n      expect(find.text('title-3'), findsNothing);\n      expect(find.text('desc-3'), findsNothing);\n\n      print(track);\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', mockState.list[0].clone()),\n            Pin('toDo-build', mockState.list[1].clone()),\n            Pin('toDo-build', mockState.list[2].clone()),\n            Pin('toDo-build', mockState.list[3].clone()),\n            Pin('toDo-onReduce', () {\n              mockState.list[0] = toDoReducer(mockState.list[0],\n                  Action(ToDoAction.markDone, payload: mockState.list[0]));\n              return mockState.list[0].clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', mockState.list[0].clone()),\n            Pin('toDo-onReduce', () {\n              mockState.list[1] = toDoReducer(mockState.list[1],\n                  Action(ToDoAction.markDone, payload: mockState.list[1]));\n              return mockState.list[1].clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', mockState.list[1].clone()),\n            Pin('adapter-onReduce', () {\n              mockState = toDoListReducer(mockState,\n                  Action(ToDoListAction.remove, payload: mockState.list[2]));\n              return mockState.clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('adapter-onReduce', () {\n              mockState = toDoListReducer(mockState,\n                  Action(ToDoListAction.remove, payload: mockState.list[2]));\n              return mockState.clone();\n            }),\n            Pin('page-build', mockState.clone()),\n          ]));\n    });\n\n    testWidgets('effect', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              effect: pageEffect,\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      await tester.tap(find.byKey(const ValueKey<String>('edit-0')));\n      await tester.pump();\n\n      expect(find.text('desc-0-effect'), findsNWidgets(1));\n\n      await tester.tap(find.byKey(const ValueKey<String>('edit-1')));\n      await tester.pump();\n\n      expect(find.text('desc-1-effect'), findsNWidgets(1));\n\n      await tester.tap(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump();\n\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(1));\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', mockState.list[0].clone()),\n            Pin('toDo-build', mockState.list[1].clone()),\n            Pin('toDo-build', mockState.list[2].clone()),\n            Pin('toDo-build', mockState.list[3].clone()),\n            Pin('toDo-onEdit', mockState.list[0].clone()),\n            Pin('toDo-onReduce', () {\n              final Todo toDo = mockState.list[0].clone();\n              toDo.desc = '${toDo.desc}-effect';\n              mockState.list[0] =\n                  toDoReducer(toDo, Action(ToDoAction.edit, payload: toDo));\n              return mockState.list[0].clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', mockState.list[0].clone()),\n            Pin('toDo-onEdit', mockState.list[1].clone()),\n            Pin('toDo-onReduce', () {\n              final Todo toDo = mockState.list[1].clone();\n              toDo.desc = '${toDo.desc}-effect';\n              mockState.list[1] =\n                  toDoReducer(toDo, Action(ToDoAction.edit, payload: toDo));\n              return mockState.list[1].clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', mockState.list[1].clone()),\n            Pin('adapter-onAdd', mockState.clone()),\n            Pin('adapter-onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', Todo.mock()),\n          ]));\n    });\n\n    testWidgets('effect-noReducer', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              effect: pageEffect,\n              dependencies: toDoListDependencies(track, noReducer: true))\n          .buildPage(pageInitParams)));\n\n      await tester.tap(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump();\n\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(1));\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', mockState.list[0].clone()),\n            Pin('toDo-build', mockState.list[1].clone()),\n            Pin('toDo-build', mockState.list[2].clone()),\n            Pin('toDo-build', mockState.list[3].clone()),\n            Pin('adapter-onAdd', mockState.clone()),\n            Pin('adapter-onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo-build', Todo.mock()),\n          ]));\n    });\n\n    testWidgets('broadcast', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              effect: pageEffect,\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump(Duration(seconds: 1));\n\n      print(track);\n\n      expect(track.countOfTag('toDo-onToDoBroadcast'), 4);\n      expect(track.countOfTag('adapter-onToDoBroadcast'), 1);\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump(Duration(seconds: 1));\n\n      expect(track.countOfTag('toDo-onToDoBroadcast'), 4);\n      expect(track.countOfTag('adapter-onToDoBroadcast'), 1);\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-1')));\n      await tester.pump();\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump(Duration(seconds: 1));\n\n      expect(track.countOfTag('toDo-onToDoBroadcast'), 3);\n      expect(track.countOfTag('adapter-onToDoBroadcast'), 1);\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-2')));\n      await tester.pump();\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump(Duration(seconds: 1));\n\n      expect(track.countOfTag('toDo-onToDoBroadcast'), 2);\n      expect(track.countOfTag('adapter-onToDoBroadcast'), 1);\n\n      await tester.tap(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump();\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump(Duration(seconds: 1));\n\n      expect(track.countOfTag('toDo-onToDoBroadcast'), 3);\n      expect(track.countOfTag('adapter-onToDoBroadcast'), 1);\n    });\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_adapter/static_flow_adapter_test.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:test_widgets/static_flow_adapter/action.dart';\nimport 'package:test_widgets/static_flow_adapter/component.dart';\nimport 'package:test_widgets/static_flow_adapter/page.dart';\nimport 'package:test_widgets/static_flow_adapter/state.dart';\nimport 'package:test_widgets/static_flow_adapter/static_flow_adapter.dart';\nimport 'package:test_widgets/test_base.dart';\n\nimport '../instrument.dart';\nimport '../track.dart';\n\nclass ToDoComponentInstrument extends TestComponent<Todo> {\n  ToDoComponentInstrument(final Track track, int index,\n      {bool hasReducer = true})\n      : super(\n            view: instrumentView<Todo>(toDoView,\n                (Todo state, Dispatch dispatch, ViewService viewService) {\n              track.append('toDo$index-build', state.clone());\n            }),\n            reducer: hasReducer\n                ? instrumentReducer<Todo>(toDoReducer,\n                    change: (Todo state, Action action) {\n                    track.append('toDo$index-onReduce', state.clone());\n                  })\n                : null,\n            effect: instrumentEffect<Todo>(toDoEffect,\n                (Action action, Get<Todo> getState) {\n              if (action.type == ToDoAction.onEdit) {\n                track.append('toDo$index-onEdit', getState().clone());\n              } else if (action.type == ToDoAction.broadcast) {\n                track.append('toDo$index-onToDoBroadcast', getState().clone());\n              }\n            }),\n            shouldUpdate: shouldUpdate);\n}\n\nclass ToDoAdapterInstrument extends TestAdapter<Todo> {\n  ToDoAdapterInstrument(final Track track, int index, {bool hasReducer = true})\n      : super(\n          adapter: asAdapter(instrumentView<Todo>(toDoView,\n              (Todo state, Dispatch dispatch, ViewService viewService) {\n            track.append('toDo$index-build', state.clone());\n          })),\n          reducer: hasReducer\n              ? instrumentReducer<Todo>(toDoReducer,\n                  change: (Todo state, Action action) {\n                  track.append('toDo$index-onReduce', state.clone());\n                })\n              : null,\n          effect: instrumentEffect<Todo>(toDoEffect,\n              (Action action, Get<Todo> getState) {\n            if (action.type == ToDoAction.onEdit) {\n              track.append('toDo$index-onEdit', getState().clone());\n            } else if (action.type == ToDoAction.broadcast) {\n              track.append('toDo$index-onToDoBroadcast', getState().clone());\n            }\n          }),\n        );\n}\n\nclass Component0 extends ToDoComponentInstrument {\n  Component0(final Track track) : super(track, 0);\n}\n\nclass Adapter1 extends ToDoAdapterInstrument {\n  Adapter1(final Track track) : super(track, 1);\n}\n\nclass Component2 extends ToDoComponentInstrument {\n  Component2(final Track track) : super(track, 2, hasReducer: false);\n}\n\nclass Adapter3 extends ToDoAdapterInstrument {\n  Adapter3(final Track track) : super(track, 3, hasReducer: false);\n}\n\nDependencies<ToDoList> toDoListDependencies(final Track track) =>\n    Dependencies<ToDoList>(\n        adapter: NoneConn<ToDoList>() +\n            TestStaticFlowAdapter<ToDoList>(\n                slots: [\n                  ConnOp<ToDoList, Todo>(\n                          get: (ToDoList toDoList) => toDoList.list[0],\n                          set: (ToDoList toDoList, Todo toDo) =>\n                              toDoList.list[0] = toDo) +\n                      Component0(track),\n                  ConnOp<ToDoList, Todo>(\n                          get: (ToDoList toDoList) => toDoList.list[1],\n                          set: (ToDoList toDoList, Todo toDo) =>\n                              toDoList.list[1] = toDo) +\n                      Adapter1(track),\n                  ConnOp<ToDoList, Todo>(\n                          get: (ToDoList toDoList) => toDoList.list[2],\n                          set: (ToDoList toDoList, Todo toDo) =>\n                              toDoList.list[2] = toDo) +\n                      Component2(track),\n                  ConnOp<ToDoList, Todo>(\n                          get: (ToDoList toDoList) => toDoList.list[3],\n                          set: (ToDoList toDoList, Todo toDo) =>\n                              toDoList.list[3] = toDo) +\n                      Adapter3(track)\n                ],\n                reducer: instrumentReducer<ToDoList>(toDoListReducer,\n                    change: (ToDoList state, Action action) {\n                  track.append('adapter-onReduce', state.clone());\n                }),\n                effect: instrumentEffect<ToDoList>(toDoListEffect,\n                    (Action action, Get<ToDoList> getState) {\n                  if (action.type == ToDoListAction.onAdd) {\n                    track.append('adapter-onAdd', getState().clone());\n                  } else if (action.type == ToDoAction.broadcast) {\n                    track.append('adapter-onToDoBroadcast', getState().clone());\n                  }\n                })));\n\nvoid main() {\n  group('static_flow_adapter', () {\n    test('create', () {\n      final Track track = Track();\n      final TestComponent<Todo> component = ToDoComponentInstrument(track, 0);\n      expect(component, isNotNull);\n\n      Widget page = TestPage<ToDoList, Map>(\n              initState: initState,\n              view: pageView,\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams);\n      expect(page, isNotNull);\n    });\n\n    testWidgets('build', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      expect(find.byKey(const ValueKey<String>('mark-0')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-0')), findsOneWidget);\n      expect(find.text('desc-0'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-0')), findsOneWidget);\n      expect(find.text('title-0'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-1')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-1')), findsOneWidget);\n      expect(find.text('desc-1'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-1')), findsOneWidget);\n      expect(find.text('title-1'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-2')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-2')), findsOneWidget);\n      expect(find.text('desc-2'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-2')), findsOneWidget);\n      expect(find.text('title-2'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-3')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-3')), findsOneWidget);\n      expect(find.text('desc-3'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-3')), findsOneWidget);\n      expect(find.text('title-3'), findsOneWidget);\n\n      expect(find.text('mark\\ndone'), findsNWidgets(3));\n      expect(find.text('done'), findsOneWidget);\n\n      expect(track.countOfTag('page-build'), 1);\n      expect(track.countOfTag('toDo0-build'), 1);\n      expect(track.countOfTag('toDo1-build'), 1);\n      expect(track.countOfTag('toDo2-build'), 1);\n      expect(track.countOfTag('toDo3-build'), 1);\n\n      track.reset();\n    });\n\n    testWidgets('reducer', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      await tester.tap(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump();\n\n      expect(find.text('mark\\ndone'), findsNWidgets(2));\n      expect(find.text('done'), findsNWidgets(2));\n\n      await tester.tap(find.byKey(const ValueKey<String>('mark-1')));\n      await tester.pump();\n\n      expect(find.text('mark\\ndone'), findsNWidgets(1));\n      expect(find.text('done'), findsNWidgets(3));\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-2')));\n      await tester.pump();\n\n      expect(find.text('desc-2'), findsNothing);\n      expect(find.byKey(const ValueKey<String>('remove-2')), findsNothing);\n      expect(find.text('title-2'), findsNothing);\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-3')));\n      await tester.pump();\n\n      expect(find.text('desc-3'), findsNothing);\n      expect(find.byKey(const ValueKey<String>('remove-3')), findsNothing);\n      expect(find.text('title-3'), findsNothing);\n\n//      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n//      expect(\n//          track,\n//          Track.pins([\n//            Pin('page-build', mockState.clone()),\n//            Pin('toDo0-build', mockState.list[0].clone()),\n//            Pin('toDo1-build', mockState.list[1].clone()),\n//            Pin('toDo2-build', mockState.list[2].clone()),\n//            Pin('toDo3-build', mockState.list[3].clone()),\n//            Pin('toDo0-onReduce', () {\n//              mockState.list[0] = toDoReducer(mockState.list[0],\n//                      Action(ToDoAction.markDone, payload: mockState.list[0]))\n//                  .clone();\n//              return mockState.list[0].clone();\n//            }),\n//            Pin('page-build', mockState.clone()),\n//            Pin('toDo0-build', mockState.list[0].clone()),\n//            Pin('toDo1-build', mockState.list[1].clone()),\n//            Pin('toDo3-build', mockState.list[3].clone()),\n//            Pin('toDo1-onReduce', () {\n//              mockState.list[1] = toDoReducer(mockState.list[1],\n//                      Action(ToDoAction.markDone, payload: mockState.list[1]))\n//                  .clone();\n//              return mockState.list[1].clone();\n//            }),\n//            Pin('page-build', mockState.clone()),\n//            Pin('toDo1-build', mockState.list[1].clone()),\n//            Pin('toDo3-build', mockState.list[3].clone()),\n//            Pin('adapter-onReduce', () {\n//              mockState = toDoListReducer(mockState,\n//                  Action(ToDoListAction.remove, payload: mockState.list[2]));\n//              return mockState.clone();\n//            }),\n//            Pin('page-build', mockState.clone()),\n//            Pin('toDo2-build', mockState.list[2].clone()),\n//            Pin('adapter-onReduce', () {\n//              mockState = toDoListReducer(mockState,\n//                  Action(ToDoListAction.remove, payload: mockState.list[3]));\n//              return mockState.clone();\n//            }),\n//            Pin('page-build', mockState.clone()),\n//            Pin('toDo3-build', mockState.list[3].clone()),\n//          ]));\n    });\n\n    testWidgets('effect', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      await tester.tap(find.byKey(const ValueKey<String>('edit-0')));\n      await tester.pump();\n\n      expect(find.text('desc-0-effect'), findsNWidgets(1));\n\n      await tester.tap(find.byKey(const ValueKey<String>('edit-1')));\n      await tester.pump();\n\n      expect(find.text('desc-1-effect'), findsNWidgets(1));\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('page-build', mockState.clone()),\n            Pin('toDo0-build', mockState.list[0].clone()),\n            Pin('toDo1-build', mockState.list[1].clone()),\n            Pin('toDo2-build', mockState.list[2].clone()),\n            Pin('toDo3-build', mockState.list[3].clone()),\n            Pin('toDo0-onEdit', mockState.list[0].clone()),\n            Pin('toDo0-onReduce', () {\n              String desc = '${mockState.list[0].desc}-effect';\n              mockState.list[0] = mockState.list[0].clone()..desc = desc;\n              return mockState.list[0].clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo0-build', mockState.list[0].clone()),\n            // Pin('toDo1-build', mockState.list[1].clone()),\n            // Pin('toDo3-build', mockState.list[3].clone()),\n            Pin('toDo1-onEdit', mockState.list[1].clone()),\n            Pin('toDo1-onReduce', () {\n              String desc = '${mockState.list[1].desc}-effect';\n              mockState.list[1] = mockState.list[1].clone()..desc = desc;\n              return mockState.list[1].clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo1-build', mockState.list[1].clone()),\n            // Pin('toDo3-build', mockState.list[3].clone()),\n          ]));\n    });\n\n    testWidgets('broadcast', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump(Duration(seconds: 1));\n\n      print(track);\n\n      expect(track.countOfTag('toDo0-onToDoBroadcast'), 1);\n      expect(track.countOfTag('toDo1-onToDoBroadcast'), 1);\n      expect(track.countOfTag('toDo2-onToDoBroadcast'), 1);\n      expect(track.countOfTag('toDo3-onToDoBroadcast'), 1);\n      expect(track.countOfTag('adapter-onToDoBroadcast'), 1);\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('mark-1')));\n      await tester.pump(Duration(seconds: 1));\n\n      expect(track.countOfTag('toDo0-onToDoBroadcast'), 1);\n      expect(track.countOfTag('toDo1-onToDoBroadcast'), 1);\n      expect(track.countOfTag('toDo2-onToDoBroadcast'), 1);\n      expect(track.countOfTag('toDo3-onToDoBroadcast'), 1);\n      expect(track.countOfTag('adapter-onToDoBroadcast'), 1);\n    });\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_aop/memoize_test.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nclass _Pair {\n  int value;\n  String label;\n  List<Object> children;\n\n  _Pair(this.value, this.label);\n}\n\n_Pair mkPair(int value, String label) => _Pair(value, label);\n\nvoid main() {\n  group('memoize_test', () {\n    test('memoize_withTwo_test', () {\n      final _Pair Function(int, String) memoize2 =\n          AOP(<ApplyLikeEnhancer>[memoize()]).withTwo(mkPair);\n\n      final _Pair p0 = memoize2(8, 'hello');\n      expect(p0.value == 8, isTrue);\n      expect(p0.label == 'hello', isTrue);\n\n      final _Pair p1 = memoize2(8, 'hello');\n      expect(p0 == p1, isTrue);\n\n      final _Pair p2 = memoize2(9, 'hello');\n      expect(p2.value == 9, isTrue);\n      expect(p2.label == 'hello', isTrue);\n\n      expect(p0 == p2, isFalse);\n    });\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_aop/redux_aop_test.dart",
    "content": "import 'package:test/test.dart';\n\nimport 'memoize_test.dart' as memoize;\n\nvoid main() {\n  group('redux_aop_test', () {\n    memoize.main();\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_component/auto_dispose_test.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  group('auto_dispose', () {\n    test('create', () {\n      final AutoDispose autoDispose = AutoDispose();\n      expect(autoDispose, isNotNull);\n      expect(autoDispose.isDisposed, isFalse);\n    });\n\n    test('dispose', () {\n      final AutoDispose autoDispose = AutoDispose();\n      expect(autoDispose, isNotNull);\n      expect(autoDispose.isDisposed, isFalse);\n      autoDispose.dispose();\n      expect(autoDispose.isDisposed, isTrue);\n    });\n\n    test('follow', () {\n      final AutoDispose parent = AutoDispose();\n      expect(parent.isDisposed, isFalse);\n\n      final AutoDispose follow0 = AutoDispose();\n      follow0.setParent(parent);\n      expect(follow0.isDisposed, isFalse);\n      follow0.dispose();\n      expect(follow0.isDisposed, isTrue);\n      expect(parent.isDisposed, isFalse);\n\n      final AutoDispose follow1 = AutoDispose();\n      follow1.setParent(parent);\n      expect(follow1.isDisposed, isFalse);\n\n      parent.dispose();\n      expect(parent.isDisposed, isTrue);\n      expect(follow1.isDisposed, isTrue);\n\n      final AutoDispose follow2 = AutoDispose();\n      follow2.setParent(parent);\n      expect(follow2.isDisposed, isTrue);\n    });\n\n    test('refollow', () {\n      final AutoDispose parent0 = AutoDispose();\n      final AutoDispose parent1 = AutoDispose();\n      expect(parent0.isDisposed, isFalse);\n      expect(parent1.isDisposed, isFalse);\n\n      final AutoDispose follow0 = AutoDispose();\n      follow0.setParent(parent0);\n      expect(follow0.isDisposed, isFalse);\n\n      follow0.setParent(parent1);\n      expect(follow0.isDisposed, isFalse);\n\n      parent0.dispose();\n      expect(parent0.isDisposed, isTrue);\n      expect(follow0.isDisposed, isFalse);\n\n      parent1.dispose();\n      expect(parent1.isDisposed, isTrue);\n      expect(follow0.isDisposed, isTrue);\n    });\n\n    test('follower', () {\n      final AutoDispose parent = AutoDispose();\n      expect(parent.isDisposed, isFalse);\n\n      final AutoDispose follow0 = parent.registerOnDisposed(null);\n      expect(follow0.isDisposed, isFalse);\n      follow0.dispose();\n      expect(follow0.isDisposed, isTrue);\n      expect(parent.isDisposed, isFalse);\n\n      final AutoDispose follow1 = parent.registerOnDisposed(null);\n      expect(follow1.isDisposed, isFalse);\n\n      parent.dispose();\n      expect(parent.isDisposed, isTrue);\n      expect(follow1.isDisposed, isTrue);\n\n      final AutoDispose follow2 = parent.registerOnDisposed(null);\n      expect(follow2.isDisposed, isTrue);\n    });\n\n    test('onDisposed', () {\n      int pCount = 0;\n      final AutoDispose parent = AutoDispose()\n        ..onDisposed(() {\n          pCount++;\n        });\n      expect(parent.isDisposed, isFalse);\n      expect(pCount, equals(0));\n\n      int fCount = 0;\n      final AutoDispose follow = parent.registerOnDisposed(() {\n        fCount++;\n      });\n      expect(fCount, equals(0));\n\n      follow.dispose();\n      expect(fCount, equals(1));\n      expect(pCount, equals(0));\n\n      parent.dispose();\n      expect(fCount, equals(1));\n      expect(pCount, equals(1));\n\n      follow.dispose();\n      expect(fCount, equals(1));\n      expect(pCount, equals(1));\n\n      parent.dispose();\n      expect(fCount, equals(1));\n      expect(pCount, equals(1));\n    });\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_component/component_test.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:test_widgets/component/action.dart';\nimport 'package:test_widgets/component/component.dart';\nimport 'package:test_widgets/component/page.dart';\nimport 'package:test_widgets/component/state.dart';\nimport 'package:test_widgets/test_base.dart';\n\nimport '../instrument.dart';\nimport '../track.dart';\n\nclass ToDoComponentInstrument extends TestComponent<Todo> {\n  ToDoComponentInstrument(final Track track, int index,\n      {bool hasReducer = true})\n      : super(\n            view: instrumentView<Todo>(toDoView,\n                (Todo state, Dispatch dispatch, ViewService viewService) {\n              track.append('toDo$index-build', state.clone());\n            }),\n            reducer: hasReducer\n                ? instrumentReducer<Todo>(toDoReducer,\n                    change: (Todo state, Action action) {\n                    track.append('toDo$index-onReduce', state.clone());\n                  })\n                : null,\n            effect: instrumentEffect<Todo>(toDoEffect,\n                (Action action, Get<Todo> getState) {\n              if (action.type == ToDoAction.onEdit) {\n                track.append('toDo$index-onEdit', getState().clone());\n              } else if (action.type == ToDoAction.broadcast) {\n                track.append('toDo$index-onToDoBroadcast', getState().clone());\n              } else if (action.type == ToDoListAction.broadcast) {\n                track.append('toDo$index-onPageBroadcast', getState().clone());\n              }\n            }),\n            shouldUpdate: shouldUpdate);\n}\n\nclass Component0 extends ToDoComponentInstrument {\n  Component0(final Track track) : super(track, 0);\n}\n\nclass Component1 extends ToDoComponentInstrument {\n  Component1(final Track track) : super(track, 1);\n}\n\nclass Component2 extends ToDoComponentInstrument {\n  Component2(final Track track) : super(track, 2);\n}\n\nclass Component3 extends ToDoComponentInstrument {\n  Component3(final Track track) : super(track, 3, hasReducer: false);\n}\n\nDependencies<ToDoList> toDoListDependencies(final Track track) =>\n    Dependencies<ToDoList>(slots: {\n      'toDo0': ConnOp<ToDoList, Todo>(\n              get: (ToDoList toDoList) => toDoList.list[0],\n              set: (ToDoList toDoList, Todo toDo) => toDoList.list[0] = toDo) +\n          Component0(track),\n      'toDo1': ConnOp<ToDoList, Todo>(\n              get: (ToDoList toDoList) => toDoList.list[1],\n              set: (ToDoList toDoList, Todo toDo) => toDoList.list[1] = toDo) +\n          Component1(track),\n      'toDo2': ConnOp<ToDoList, Todo>(\n              get: (ToDoList toDoList) => toDoList.list[2],\n              set: (ToDoList toDoList, Todo toDo) => toDoList.list[2] = toDo) +\n          Component2(track),\n      'toDo3': ConnOp<ToDoList, Todo>(\n              get: (ToDoList toDoList) => toDoList.list[3],\n              set: (ToDoList toDoList, Todo toDo) => toDoList.list[3] = toDo) +\n          Component3(track),\n    });\n\nWidget pageView(\n  ToDoList state,\n  Dispatch dispatch,\n  ViewService viewService,\n) {\n  return Column(\n    children: <Widget>[\n      Expanded(\n          child: ListView.builder(\n        itemBuilder: (context, index) {\n          if (index == 0) {\n            return viewService.buildComponent('toDo0');\n          } else if (index == 1) {\n            return viewService.buildComponent('toDo1');\n          } else if (index == 2) {\n            return viewService.buildComponent('toDo2');\n          } else if (index == 3) {\n            return viewService.buildComponent('toDo3');\n          } else {\n            final Todo toDo = state.list[index];\n            return Container(\n              padding: const EdgeInsets.all(8.0),\n              margin: const EdgeInsets.all(8.0),\n              color: Colors.grey,\n              child: Text(toDo.desc),\n              alignment: AlignmentDirectional.center,\n            );\n          }\n        },\n        itemCount: state.list.length,\n      )),\n      Row(\n        children: <Widget>[\n          Expanded(\n              child: GestureDetector(\n            child: Container(\n              key: const ValueKey('Add'),\n              height: 68.0,\n              color: Colors.green,\n              alignment: AlignmentDirectional.center,\n              child: const Text('Add'),\n            ),\n            onTap: () {\n              print('dispatch Add');\n              dispatch(const Action(ToDoListAction.onAdd));\n            },\n            onLongPress: () {\n              print('dispatch broadcast');\n              dispatch(const Action(ToDoListAction.onBroadcast));\n            },\n          )),\n        ],\n      )\n    ],\n  );\n}\n\nvoid main() {\n  group('component', () {\n    test('create', () {\n      final TestComponent<Todo> component = TestComponent<Todo>(\n          view: toDoView, wrapper: (child) => ComponentWrapper(child));\n      expect(component, isNotNull);\n\n      /// TODO\n      final Widget componentWidget = component.buildComponent(\n        createBatchStore<Todo>(Todo.mock(), null),\n        () => Todo.mock(),\n        enhancer: EnhancerDefault<Object>(),\n        bus: DispatchBusDefault(),\n      );\n      expect(componentWidget, isNotNull);\n\n      expect(\n          const TypeMatcher<ComponentWrapper>().check(componentWidget), isTrue);\n    });\n\n    testWidgets('build', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              reducer: toDoListReducer,\n              effect: toDoListEffect,\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      expect(find.byKey(const ValueKey<String>('Add')), findsOneWidget);\n      expect(find.text('Add'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-0')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-0')), findsOneWidget);\n      expect(find.text('desc-0'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-0')), findsOneWidget);\n      expect(find.text('title-0'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-1')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-1')), findsOneWidget);\n      expect(find.text('desc-1'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-1')), findsOneWidget);\n      expect(find.text('title-1'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-2')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-2')), findsOneWidget);\n      expect(find.text('desc-2'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-2')), findsOneWidget);\n      expect(find.text('title-2'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-3')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-3')), findsOneWidget);\n      expect(find.text('desc-3'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-3')), findsOneWidget);\n      expect(find.text('title-3'), findsOneWidget);\n\n      expect(find.text('mark\\ndone'), findsNWidgets(3));\n      expect(find.text('done'), findsOneWidget);\n\n      expect(track.countOfTag('page-build'), 1);\n      expect(track.countOfTag('toDo0-build'), 1);\n      expect(track.countOfTag('toDo1-build'), 1);\n      expect(track.countOfTag('toDo2-build'), 1);\n      expect(track.countOfTag('toDo3-build'), 1);\n\n      track.reset();\n    });\n\n    testWidgets('reducer', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              reducer: instrumentReducer<ToDoList>(toDoListReducer,\n                  change: (ToDoList state, Action action) {\n                track.append('page-onReduce', state.clone());\n              }),\n              effect: instrumentEffect<ToDoList>(toDoListEffect,\n                  (Action action, Get<ToDoList> getState) {\n                if (action.type == ToDoListAction.onAdd) {\n                  track.append('page-onAdd', getState().clone());\n                }\n              }),\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      await tester.tap(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump();\n\n      expect(find.text('mark\\ndone'), findsNWidgets(2));\n      expect(find.text('done'), findsNWidgets(2));\n\n      await tester.tap(find.byKey(const ValueKey<String>('mark-1')));\n      await tester.pump();\n\n      expect(find.text('mark\\ndone'), findsNWidgets(1));\n      expect(find.text('done'), findsNWidgets(3));\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-2')));\n      await tester.pump();\n\n      expect(find.text('removed'), findsNWidgets(1));\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-3')));\n      await tester.pump();\n\n      expect(find.text('removed'), findsNWidgets(2));\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('page-build', mockState.clone()),\n            Pin('toDo0-build', mockState.list[0].clone()),\n            Pin('toDo1-build', mockState.list[1].clone()),\n            Pin('toDo2-build', mockState.list[2].clone()),\n            Pin('toDo3-build', mockState.list[3].clone()),\n            Pin('toDo0-onReduce', () {\n              mockState.list[0] = mockState.list[0].clone()..isDone = true;\n              return mockState.list[0].clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo0-build', mockState.list[0].clone()),\n            Pin('toDo1-onReduce', () {\n              mockState.list[1] = mockState.list[1].clone()..isDone = true;\n              return mockState.list[1].clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo1-build', mockState.list[1].clone()),\n            Pin('page-onReduce', () {\n              mockState.list[2] = mockState.list[2].clone()..desc = 'removed';\n              return mockState.clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo2-build', mockState.list[2].clone()),\n            Pin('page-onReduce', () {\n              mockState.list[3] = mockState.list[3].clone()..desc = 'removed';\n              return mockState.clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo3-build', mockState.list[3].clone()),\n          ]));\n    });\n\n    testWidgets('effect', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              reducer: instrumentReducer<ToDoList>(toDoListReducer,\n                  change: (ToDoList state, Action action) {\n                track.append('page-onReduce', state.clone());\n              }),\n              effect: instrumentEffect<ToDoList>(toDoListEffect,\n                  (Action action, Get<ToDoList> getState) {\n                if (action.type == ToDoListAction.onAdd) {\n                  track.append('page-onAdd', getState().clone());\n                }\n              }),\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      await tester.tap(find.byKey(const ValueKey<String>('edit-0')));\n      await tester.pump();\n\n      expect(find.text('desc-0-effect'), findsNWidgets(1));\n\n      await tester.tap(find.byKey(const ValueKey<String>('edit-1')));\n      await tester.pump();\n\n      expect(find.text('desc-1-effect'), findsNWidgets(1));\n\n      await tester.tap(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump();\n\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(1));\n\n      await tester.tap(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump();\n\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(2));\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('page-build', mockState.clone()),\n            Pin('toDo0-build', mockState.list[0].clone()),\n            Pin('toDo1-build', mockState.list[1].clone()),\n            Pin('toDo2-build', mockState.list[2].clone()),\n            Pin('toDo3-build', mockState.list[3].clone()),\n            Pin('toDo0-onEdit', mockState.list[0].clone()),\n            Pin('toDo0-onReduce', () {\n              String desc = '${mockState.list[0].desc}-effect';\n              mockState.list[0] = mockState.list[0].clone()..desc = desc;\n              return mockState.list[0].clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo0-build', mockState.list[0].clone()),\n            Pin('toDo1-onEdit', mockState.list[1].clone()),\n            Pin('toDo1-onReduce', () {\n              String desc = '${mockState.list[1].desc}-effect';\n              mockState.list[1] = mockState.list[1].clone()..desc = desc;\n              return mockState.list[1].clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('toDo1-build', mockState.list[1].clone()),\n            Pin('page-onAdd', mockState.clone()),\n            Pin('page-onReduce', () {\n              mockState.list.add(Todo.mock());\n              return mockState.clone();\n            }),\n            Pin('page-build', mockState.clone()),\n            Pin('page-onAdd', mockState.clone()),\n            Pin('page-onReduce', () {\n              mockState.list.add(Todo.mock());\n              return mockState.clone();\n            }),\n            Pin('page-build', mockState.clone()),\n          ]));\n    });\n\n    testWidgets('broadcast', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: initState,\n              view: instrumentView<ToDoList>(pageView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('page-build', state.clone());\n              }),\n              reducer: instrumentReducer<ToDoList>(toDoListReducer,\n                  change: (ToDoList state, Action action) {\n                track.append('page-onReduce', state.clone());\n              }),\n              effect: instrumentEffect<ToDoList>(toDoListEffect,\n                  (Action action, Get<ToDoList> getState) {\n                if (action.type == ToDoListAction.onAdd) {\n                  track.append('page-onAdd', getState().clone());\n                } else if (action.type == ToDoAction.broadcast) {\n                  track.append('page-onToDoBroadcast', getState().clone());\n                } else if (action.type == ToDoListAction.broadcast) {\n                  track.append('page-onPageBroadcast', getState().clone());\n                }\n              }),\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump(Duration(seconds: 1));\n\n      print(track);\n\n      expect(track.countOfTag('toDo0-onToDoBroadcast'), 1);\n      expect(track.countOfTag('toDo1-onToDoBroadcast'), 1);\n      expect(track.countOfTag('toDo2-onToDoBroadcast'), 1);\n      expect(track.countOfTag('toDo3-onToDoBroadcast'), 1);\n      expect(track.countOfTag('page-onToDoBroadcast'), 1);\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('mark-1')));\n      await tester.pump(Duration(seconds: 1));\n\n      expect(track.countOfTag('toDo0-onToDoBroadcast'), 1);\n      expect(track.countOfTag('toDo1-onToDoBroadcast'), 1);\n      expect(track.countOfTag('toDo2-onToDoBroadcast'), 1);\n      expect(track.countOfTag('toDo3-onToDoBroadcast'), 1);\n      expect(track.countOfTag('page-onToDoBroadcast'), 1);\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump(Duration(seconds: 1));\n\n      expect(track.countOfTag('toDo0-onPageBroadcast'), 1);\n      expect(track.countOfTag('toDo1-onPageBroadcast'), 1);\n      expect(track.countOfTag('toDo2-onPageBroadcast'), 1);\n      expect(track.countOfTag('toDo3-onPageBroadcast'), 1);\n      expect(track.countOfTag('page-onPageBroadcast'), 1);\n\n      track.reset();\n      await tester.longPress(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump(Duration(seconds: 1));\n\n      expect(track.countOfTag('toDo0-onPageBroadcast'), 1);\n      expect(track.countOfTag('toDo1-onPageBroadcast'), 1);\n      expect(track.countOfTag('toDo2-onPageBroadcast'), 1);\n      expect(track.countOfTag('toDo3-onPageBroadcast'), 1);\n      expect(track.countOfTag('page-onPageBroadcast'), 1);\n    });\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_component/lifecycle_test.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:test_widgets/component/action.dart';\nimport 'package:test_widgets/component/component.dart';\nimport 'package:test_widgets/component/page.dart';\nimport 'package:test_widgets/component/state.dart';\nimport 'package:test_widgets/test_base.dart';\n\nimport '../instrument.dart';\nimport '../track.dart';\n\nclass ToDoComponentInstrument extends TestComponent<Todo> {\n  ToDoComponentInstrument(final Track track)\n      : super(\n            view: toDoView,\n            reducer: toDoReducer,\n            effect: instrumentEffect<Todo>(toDoEffect,\n                (Action action, Get<Todo> getState) {\n              if (action.type == ToDoAction.onEdit) {\n                track.append('toDo-onEdit');\n                print('toDo-onEdit');\n              } else if (action.type == Lifecycle.initState) {\n                track.append('toDo-initState');\n                print('toDo-initState');\n              } else if (action.type == Lifecycle.build) {\n                track.append('toDo-build');\n                print('toDo-build');\n              } else if (action.type == Lifecycle.deactivate) {\n                track.append('toDo-deactivate');\n                print('toDo-deactivate');\n              } else if (action.type == Lifecycle.didChangeDependencies) {\n                track.append('toDo-didChangeDependencies');\n                print('toDo-didChangeDependencies');\n              } else if (action.type == Lifecycle.didUpdateWidget) {\n                track.append('toDo-didUpdateWidget');\n                print('toDo-didUpdateWidget');\n              } else if (action.type == Lifecycle.dispose) {\n                track.append('toDo-dispose');\n                print('toDo-dispose');\n              }\n            }),\n            shouldUpdate: shouldUpdate);\n}\n\nDependencies<ToDoList> toDoListDependencies(final Track track) =>\n    Dependencies<ToDoList>(slots: {\n      'toDo': ConnOp<ToDoList, Todo>(\n              get: (ToDoList toDoList) =>\n                  toDoList.list.isNotEmpty ? toDoList.list[0] : Todo.mock(),\n              set: (ToDoList toDoList, Todo toDo) => toDoList.list.isNotEmpty\n                  ? toDoList.list[0] = toDo\n                  : toDoList) +\n          ToDoComponentInstrument(track)\n    });\n\nWidget pageView(\n  ToDoList state,\n  Dispatch dispatch,\n  ViewService viewService,\n) {\n  return Column(\n    children: <Widget>[\n      Expanded(\n          child: ListView.builder(\n        itemBuilder: (context, index) => viewService.buildComponent('toDo'),\n        itemCount: state.list.length,\n      )),\n      Row(\n        children: <Widget>[\n          Expanded(\n              child: GestureDetector(\n            child: Container(\n              key: const ValueKey('Add'),\n              height: 68.0,\n              color: Colors.green,\n              alignment: AlignmentDirectional.center,\n              child: const Text('Add'),\n            ),\n            onTap: () {\n              print('dispatch Add');\n              dispatch(const Action(ToDoListAction.onAdd));\n            },\n            onLongPress: () {\n              print('dispatch broadcast');\n              dispatch(const Action(ToDoListAction.onBroadcast));\n            },\n          )),\n        ],\n      )\n    ],\n  );\n}\n\nvoid main() {\n  group('component', () {\n    test('create', () {\n      final TestComponent<Todo> component = TestComponent<Todo>(\n          view: toDoView, wrapper: (child) => ComponentWrapper(child));\n      expect(component, isNotNull);\n\n      /// TODO\n      final Widget componentWidget = component.buildComponent(\n        createBatchStore<Todo>(Todo.mock(), null),\n        () => Todo.mock(),\n        bus: DispatchBusDefault(),\n        enhancer: EnhancerDefault<Object>(),\n      );\n      expect(componentWidget, isNotNull);\n\n      expect(\n          const TypeMatcher<ComponentWrapper>().check(componentWidget), isTrue);\n    });\n\n    testWidgets('cycleLife', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState: (Map map) {\n                final ToDoList toDoList = initState(map);\n                final ToDoList state = ToDoList();\n                state.list.add(toDoList.list[0]);\n                return state;\n              },\n              view: pageView,\n              reducer: (ToDoList state, Action action) {\n                if (action.type == ToDoListAction.remove) {\n                  final ToDoList newState = state.clone();\n                  newState.list.clear();\n                  return newState;\n                } else {\n                  return toDoListReducer(state, action);\n                }\n              },\n              effect: toDoListEffect,\n              dependencies: toDoListDependencies(track))\n          .buildPage(pageInitParams)));\n\n      expect(find.byKey(const ValueKey<String>('Add')), findsOneWidget);\n      expect(find.text('Add'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-0')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-0')), findsOneWidget);\n      expect(find.text('desc-0'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-0')), findsOneWidget);\n\n      await tester.tap(find.byKey(const ValueKey<String>('edit-0')));\n      await tester.pump();\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-0')));\n      await tester.pump();\n\n      expect(find.byKey(const ValueKey<String>('mark-0')), findsNothing);\n      expect(find.byKey(const ValueKey<String>('edit-0')), findsNothing);\n      expect(find.byKey(const ValueKey<String>('remove-0')), findsNothing);\n\n      expect(\n          track,\n          Track.pins([\n            Pin('toDo-initState'),\n            Pin('toDo-didChangeDependencies'),\n            Pin('toDo-build'),\n            Pin('toDo-onEdit'),\n            Pin('toDo-didUpdateWidget'),\n            Pin('toDo-build'),\n            Pin('toDo-deactivate'),\n            Pin('toDo-dispose')\n          ]));\n    });\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_component/page_test.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:test_widgets/page/action.dart';\nimport 'package:test_widgets/page/page.dart';\nimport 'package:test_widgets/page/state.dart';\nimport 'package:test_widgets/test_base.dart';\n\nimport '../instrument.dart';\nimport '../track.dart';\n\nvoid main() {\n  group('page', () {\n    test('create', () {\n      TestPage<ToDoList, Map> page = TestPage<ToDoList, Map>(\n          initState: initState,\n          view: toDoListView,\n          wrapper: (Widget child) => PageWrapper(child));\n      expect(page, isNotNull);\n\n      /// TODO\n      final Widget pageWidget = page.buildPage(pageInitParams);\n      expect(pageWidget, isNotNull);\n\n      expect(const TypeMatcher<PageWrapper>().check(pageWidget), isTrue);\n      //expect(pageWidget, TypeMatcher<PageWrapper>());\n    });\n\n    testWidgets('build', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n          initState: instrumentInitState<ToDoList, Map>(initState, pre: (map) {\n            track.append('initState', map);\n          }),\n          view: instrumentView<ToDoList>(toDoListView,\n              (ToDoList state, Dispatch dispatch, ViewService viewService) {\n            track.append('build', state.clone());\n          })).buildPage(pageInitParams)));\n\n      expect(find.byKey(const ValueKey<String>('Add')), findsOneWidget);\n      expect(find.text('Add'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-0')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-0')), findsOneWidget);\n      expect(find.text('desc-0'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-0')), findsOneWidget);\n      expect(find.text('title-0'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-1')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-1')), findsOneWidget);\n      expect(find.text('desc-1'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-1')), findsOneWidget);\n      expect(find.text('title-1'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-2')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-2')), findsOneWidget);\n      expect(find.text('desc-2'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-2')), findsOneWidget);\n      expect(find.text('title-2'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('mark-3')), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('edit-3')), findsOneWidget);\n      expect(find.text('desc-3'), findsOneWidget);\n      expect(find.byKey(const ValueKey<String>('remove-3')), findsOneWidget);\n      expect(find.text('title-3'), findsOneWidget);\n\n      expect(find.text('mark\\ndone'), findsNWidgets(3));\n      expect(find.text('done'), findsOneWidget);\n\n      expect(\n          track,\n          Track.pins([\n            Pin('initState', pageInitParams),\n            Pin('build', ToDoList.fromMap(pageInitParams))\n          ]));\n    });\n\n    testWidgets('reducer', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n          initState: instrumentInitState<ToDoList, Map>(initState, pre: (map) {\n            track.append('initState', map);\n          }),\n          view: instrumentView<ToDoList>(toDoListView,\n              (ToDoList state, Dispatch dispatch, ViewService viewService) {\n            track.append('build', state.clone());\n          }),\n          reducer: instrumentReducer<ToDoList>(toDoListReducer,\n              suf: (ToDoList state, Action action) {\n            track.append('onReduce', state.clone());\n          })).buildPage(pageInitParams)));\n\n      await tester.tap(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump();\n\n      expect(find.text('mark\\ndone'), findsNWidgets(2));\n      expect(find.text('done'), findsNWidgets(2));\n\n      await tester.tap(find.byKey(const ValueKey<String>('mark-1')));\n      await tester.pump();\n\n      expect(find.text('mark\\ndone'), findsNWidgets(1));\n      expect(find.text('done'), findsNWidgets(3));\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-2')));\n      await tester.pump();\n\n      expect(find.byKey(const ValueKey<String>('remove-2')), findsNothing);\n      expect(find.text('desc-2'), findsNothing);\n      expect(find.text('title-2'), findsNothing);\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-3')));\n      await tester.pump();\n\n      expect(find.byKey(const ValueKey<String>('remove-3')), findsNothing);\n      expect(find.text('desc-3'), findsNothing);\n      expect(find.text('title-3'), findsNothing);\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('initState', pageInitParams),\n            Pin('build', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState,\n                  Action(ToDoListAction.markDone,\n                      payload: mockState.list.firstWhere((i) => i.id == '0')));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState,\n                  Action(ToDoListAction.markDone,\n                      payload: mockState.list.firstWhere((i) => i.id == '1')));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState,\n                  Action(ToDoListAction.remove,\n                      payload: mockState.list.firstWhere((i) => i.id == '2')));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState,\n                  Action(ToDoListAction.remove,\n                      payload: mockState.list.firstWhere((i) => i.id == '3')));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n          ]));\n    });\n\n    testWidgets('effect', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n          initState: instrumentInitState<ToDoList, Map>(initState, pre: (map) {\n            track.append('initState', map);\n          }),\n          view: instrumentView<ToDoList>(toDoListView,\n              (ToDoList state, Dispatch dispatch, ViewService viewService) {\n            track.append('build', state.clone());\n          }),\n          reducer: instrumentReducer<ToDoList>(toDoListReducer,\n              suf: (ToDoList state, Action action) {\n            track.append('onReduce', state.clone());\n          }),\n          effect: instrumentEffect(toDoListEffect,\n              (Action action, Get<ToDoList> getState) {\n            if (action.type == ToDoListAction.onAdd) {\n              track.append('onAdd', getState().clone());\n            } else if (action.type == ToDoListAction.onEdit) {\n              track.append('onEdit', getState().clone());\n            }\n          })).buildPage(pageInitParams)));\n\n      await tester.tap(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump();\n\n      expect(find.text('title-mock', skipOffstage: false), findsNWidgets(1));\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(1));\n\n      await tester.tap(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump();\n\n      expect(find.text('title-mock', skipOffstage: false), findsNWidgets(2));\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(2));\n\n      await tester.tap(find.byKey(const ValueKey<String>('edit-0')));\n      await tester.pump();\n\n      expect(find.text('title-0', skipOffstage: false), findsOneWidget);\n      expect(find.text('desc-0-effect', skipOffstage: false), findsOneWidget);\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('initState', pageInitParams),\n            Pin('build', mockState.clone()),\n            Pin('onAdd', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onAdd', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onEdit', mockState.clone()),\n            Pin('onReduce', () {\n              Todo toDo = mockState.list.firstWhere((i) => i.id == '0');\n              toDo = toDo.clone();\n              toDo.desc = '${toDo.desc}-effect';\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.edit, payload: toDo));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n          ]));\n    });\n\n    testWidgets('effectAsync', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n          initState: instrumentInitState<ToDoList, Map>(initState, pre: (map) {\n            track.append('initState', map);\n          }),\n          view: instrumentView<ToDoList>(toDoListView,\n              (ToDoList state, Dispatch dispatch, ViewService viewService) {\n            track.append('build', state.clone());\n          }),\n          reducer: instrumentReducer<ToDoList>(toDoListReducer,\n              suf: (ToDoList state, Action action) {\n            track.append('onReduce', state.clone());\n          }),\n          effect: instrumentEffect(toDoListEffectAsync,\n              (Action action, Get<ToDoList> getState) {\n            if (action.type == ToDoListAction.onAdd) {\n              track.append('onAdd', getState().clone());\n            } else if (action.type == ToDoListAction.onEdit) {\n              track.append('onEdit', getState().clone());\n            }\n          })).buildPage(pageInitParams)));\n\n      await tester.tap(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump(Duration(seconds: 3));\n\n      expect(find.text('title-mock', skipOffstage: false), findsNWidgets(1));\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(1));\n\n      await tester.tap(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump(Duration(seconds: 3));\n\n      expect(find.text('title-mock', skipOffstage: false), findsNWidgets(2));\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(2));\n\n      await tester.tap(find.byKey(const ValueKey<String>('edit-0')));\n      await tester.pump(Duration(seconds: 3));\n\n      expect(find.text('title-0', skipOffstage: false), findsOneWidget);\n      expect(find.text('desc-0-effect', skipOffstage: false), findsOneWidget);\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('initState', pageInitParams),\n            Pin('build', mockState.clone()),\n            Pin('onAdd', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onAdd', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onEdit', mockState.clone()),\n            Pin('onReduce', () {\n              Todo toDo = mockState.list.firstWhere((i) => i.id == '0');\n              toDo = toDo.clone();\n              toDo.desc = '${toDo.desc}-effect';\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.edit, payload: toDo));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n          ]));\n    });\n\n    testWidgets('effect', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n          initState: instrumentInitState<ToDoList, Map>(initState, pre: (map) {\n            track.append('initState', map);\n          }),\n          view: instrumentView<ToDoList>(toDoListView,\n              (ToDoList state, Dispatch dispatch, ViewService viewService) {\n            track.append('build', state.clone());\n          }),\n          reducer: instrumentReducer<ToDoList>(toDoListReducer,\n              suf: (ToDoList state, Action action) {\n            track.append('onReduce', state.clone());\n          }),\n          effect: (Action action, Context<ToDoList> ctx) => instrumentEffect(\n                  toDoListEffect, (Action action, Get<ToDoList> getState) {\n                if (action.type == ToDoListAction.onAdd) {\n                  track.append('onAdd', getState().clone());\n                } else if (action.type == ToDoListAction.onEdit) {\n                  track.append('onEdit', getState().clone());\n                }\n              })(action, ctx)).buildPage(pageInitParams)));\n\n      expect(find.byKey(const ValueKey<String>('Add')), findsOneWidget);\n      await tester.tap(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump();\n\n      expect(find.text('title-mock', skipOffstage: false), findsNWidgets(1));\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(1));\n\n      expect(find.byKey(const ValueKey<String>('Add')), findsOneWidget);\n      await tester.tap(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump();\n\n      expect(find.text('title-mock', skipOffstage: false), findsNWidgets(2));\n      expect(find.text('desc-mock', skipOffstage: false), findsNWidgets(2));\n\n      expect(find.byKey(const ValueKey<String>('edit-0')), findsOneWidget);\n      await tester.tap(find.byKey(const ValueKey<String>('edit-0')));\n      await tester.pump();\n\n      expect(find.text('title-0'), findsOneWidget);\n      expect(find.text('desc-0-effect'), findsOneWidget);\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('initState', pageInitParams),\n            Pin('build', mockState.clone()),\n            Pin('onAdd', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onAdd', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onEdit', mockState.clone()),\n            Pin('onReduce', () {\n              Todo toDo = mockState.list.firstWhere((i) => i.id == '0');\n              toDo = toDo.clone();\n              toDo.desc = '${toDo.desc}-effect';\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.edit, payload: toDo));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n          ]));\n    });\n\n    testWidgets('shouldUpdate', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n              initState:\n                  instrumentInitState<ToDoList, Map>(initState, pre: (map) {\n                track.append('initState', map);\n              }),\n              view: instrumentView<ToDoList>(toDoListView,\n                  (ToDoList state, Dispatch dispatch, ViewService viewService) {\n                track.append('build', state.clone());\n              }),\n              reducer: instrumentReducer<ToDoList>(toDoListReducer,\n                  suf: (ToDoList state, Action action) {\n                track.append('onReduce', state.clone());\n              }),\n              effect: (Action action, Context<ToDoList> ctx) =>\n                  instrumentEffect<ToDoList>(toDoListEffect,\n                      (Action action, Get<ToDoList> getState) {\n                    if (action.type == ToDoListAction.onAdd) {\n                      track.append('onAdd', getState().clone());\n                    }\n                  })(action, ctx),\n              shouldUpdate: forbidRefreshUI)\n          .buildPage(pageInitParams)));\n\n      await tester.tap(find.byKey(const ValueKey<String>('Add')));\n      await tester.pump();\n\n      expect(find.text('title-mock'), findsNothing);\n      expect(find.text('desc-mock'), findsNothing);\n\n      await tester.tap(find.byKey(const ValueKey<String>('mark-0')));\n      await tester.pump();\n\n      expect(find.text('mark\\ndone'), findsNWidgets(3));\n      expect(find.text('done'), findsOneWidget);\n\n      await tester.tap(find.byKey(const ValueKey<String>('remove-1')));\n      await tester.pump();\n\n      expect(find.byKey(const ValueKey<String>('remove-1')), findsOneWidget);\n      expect(find.text('desc-1'), findsOneWidget);\n      expect(find.text('title-1'), findsOneWidget);\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('initState', pageInitParams),\n            Pin('build', mockState.clone()),\n            Pin('onAdd', mockState.clone()),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.add, payload: Todo.mock()));\n              return mockState.clone();\n            }),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState,\n                  Action(ToDoListAction.markDone,\n                      payload: mockState.list.firstWhere((i) => i.id == '0')));\n              return mockState.clone();\n            }),\n            Pin('onReduce', () {\n              mockState = toDoListReducer(\n                  mockState,\n                  Action(ToDoListAction.remove,\n                      payload: mockState.list.firstWhere((i) => i.id == '1')));\n              return mockState.clone();\n            }),\n          ]));\n    });\n\n    /// TODO\n    testWidgets('middleware', (WidgetTester tester) async {\n      final Track track = Track();\n\n      await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n          initState: instrumentInitState<ToDoList, Map>(initState, pre: (map) {\n            track.append('initState', map);\n          }),\n          view: instrumentView<ToDoList>(toDoListView,\n              (ToDoList state, Dispatch dispatch, ViewService viewService) {\n            track.append('build', state.clone());\n          }),\n          reducer: instrumentReducer<ToDoList>(toDoListReducer,\n              suf: (ToDoList state, Action action) {\n            track.append('onReduce', state.clone());\n          }),\n          effect: toDoListEffect,\n          middleware: <Middleware<ToDoList>>[\n            instrumentMiddleware<ToDoList>(toDoListMiddleware,\n                pre: (action, getState) {\n              if (action.type == ToDoListAction.middlewareEdit) {\n                track.append('onMiddleware', getState().clone());\n              }\n            })\n          ]).buildPage(pageInitParams)));\n\n      expect(find.byKey(const ValueKey<String>('edit-0')), findsOneWidget);\n      await tester.longPress(find.byKey(const ValueKey<String>('edit-0')));\n      await tester.pump();\n\n      expect(find.text('desc-0-middleware'), findsOneWidget);\n\n      expect(find.byKey(const ValueKey<String>('edit-0')), findsOneWidget);\n      await tester.longPress(find.byKey(const ValueKey<String>('edit-0')));\n      await tester.pump();\n\n      expect(find.text('desc-0-middleware-middleware'), findsOneWidget);\n\n      ToDoList mockState = ToDoList.fromMap(pageInitParams);\n      expect(\n          track,\n          Track.pins([\n            Pin('initState', pageInitParams),\n            Pin('build', mockState.clone()),\n            Pin('onMiddleware', mockState.clone()),\n            Pin('onReduce', mockState.clone()),\n            Pin('onReduce', () {\n              Todo toDo = mockState.list.firstWhere((i) => i.id == '0');\n              toDo = toDo.clone();\n              toDo.desc = '${toDo.desc}-middleware';\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.edit, payload: toDo));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n            Pin('onMiddleware', mockState.clone()),\n            Pin('onReduce', mockState.clone()),\n            Pin('onReduce', () {\n              Todo toDo = mockState.list.firstWhere((i) => i.id == '0');\n              toDo = toDo.clone();\n              toDo.desc = '${toDo.desc}-middleware';\n              mockState = toDoListReducer(\n                  mockState, Action(ToDoListAction.edit, payload: toDo));\n              return mockState.clone();\n            }),\n            Pin('build', mockState.clone()),\n          ]));\n    });\n\n    // testWidgets('error', (WidgetTester tester) async {\n    //   final Track track = Track();\n\n    //   await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n    //       initState: initState,\n    //       view: toDoListView,\n    //       reducer: toDoListReducer,\n    //       higherEffect: toDoListHigherEffect,\n    //       onError: instrumentError<ToDoList>(toDoListErrorHandler, (exp, ctx) {\n    //         track.append('onErr', exp);\n    //       })).buildPage(pageInitParams)));\n\n    //   await tester.tap(find.byKey(const ValueKey<String>('Error')));\n    //   await tester.pump();\n\n    //   expect(\n    //       track,\n    //       Track.pins([\n    //         Pin('onErr', KnowException()),\n    //       ]));\n\n    //   //expect(exception, UnKnowException());\n    // });\n\n    // testWidgets('errorAsync', (WidgetTester tester) async {\n    //   final Track track = Track();\n\n    //   await tester.pumpWidget(TestStub(TestPage<ToDoList, Map>(\n    //       initState: initState,\n    //       view: toDoListView,\n    //       reducer: toDoListReducer,\n    //       effect: toDoListEffectAsync,\n    //       onError: instrumentError<ToDoList>(toDoListErrorHandler, (exp, ctx) {\n    //         track.append('onErr', exp);\n    //       })).buildPage(pageInitParams)));\n\n    //   await tester.tap(find.byKey(const ValueKey<String>('Error')));\n    //   await tester.pump(Duration(seconds: 3));\n\n    //   expect(\n    //       track,\n    //       Track.pins([\n    //         Pin('onErr', KnowException()),\n    //       ]));\n\n    //   //expect(exception, UnKnowException());\n    // });\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_component/redux_component_test.dart",
    "content": "import 'package:test/test.dart';\n\nimport 'auto_dispose_test.dart' as auto_dispose;\nimport 'component_test.dart' as component;\nimport 'lifecycle_test.dart' as lifecycle;\nimport 'page_test.dart' as page;\n\nvoid main() {\n  group('redux_component_test', () {\n    auto_dispose.main();\n    component.main();\n    lifecycle.main();\n    page.main();\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_connector/map_like_test.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nclass _Info extends MapLike {\n  String name;\n  int age = 0;\n\n  _Info(this.name);\n}\n\nvoid main() {\n  group('map_like_test', () {\n    test('map_like_with_key', () {\n      final _Info info = _Info('Tom');\n\n      final AutoInitConnector<_Info, String> nameConnector =\n          AutoInitConnector<_Info, String>((_Info info) => info.name,\n              key: 'name');\n\n      expect(nameConnector.get(info), equals('Tom'));\n\n      nameConnector.set(info, 'John');\n\n      expect(nameConnector.get(info), equals('John'));\n\n      final AutoInitConnector<_Info, int> ageConnector =\n          AutoInitConnector<_Info, int>((_Info info) => info.age, key: 'age');\n\n      expect(ageConnector.get(info), equals(0));\n    });\n\n    test('map_like_without_key', () {\n      final _Info info = _Info('Tom');\n\n      final AutoInitConnector<_Info, int> generatedKeyConnector =\n          AutoInitConnector<_Info, int>((_Info info) => info.age);\n\n      expect(generatedKeyConnector.get(info), equals(0));\n\n      generatedKeyConnector.set(info, 1);\n\n      expect(generatedKeyConnector.get(info), equals(1));\n    });\n\n    test('map_like_with_hook', () {\n      final _Info info = _Info('Tom');\n\n      String newValue = '';\n\n      final AutoInitConnector<_Info, String> nameConnector =\n          AutoInitConnector<_Info, String>((_Info info) => info.name,\n              key: 'name', set: (_, String value) => newValue = value);\n\n      expect(newValue, equals(''));\n      \n      nameConnector.set(info, 'John');\n\n      expect(newValue, equals('John'));\n    });\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_connector/redux_connector_test.dart",
    "content": "import 'package:test/test.dart';\n\nimport 'map_like_test.dart' as mapLike;\nimport 'reselect_test.dart' as reselect;\n\nvoid main() {\n  group('redux_connector_test', () {\n    reselect.main();\n    mapLike.main();\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_connector/reselect_test.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nclass _Parent {\n  int value;\n  String label;\n  List<Object> children;\n\n  _Parent(this.value, this.label, this.children);\n}\n\nclass _Info {\n  int value;\n  String label;\n\n  _Info(this.value, this.label);\n}\n\nclass _InfoConn extends Reselect2<_Parent, _Info, int, String> {\n  @override\n  _Info computed(int sub0, String sub1) => _Info(sub0, sub1);\n\n  @override\n  int getSub0(_Parent state) => state.value;\n\n  @override\n  String getSub1(_Parent state) => state.label;\n\n  @override\n  void set(_Parent state, _Info subState) {\n    state.value = subState.value;\n    state.label = subState.label;\n  }\n}\n\nvoid main() {\n  group('reselect_test', () {\n    final _Parent parent = _Parent(1, 'tag', null);\n    final AbstractConnector<_Parent, _Info> r2 = _InfoConn();\n\n    _Info i0, i1, i2, i3;\n    i0 = r2.get(parent);\n\n    test('reselect_init', () {\n      expect(i0.value == parent.value, isTrue);\n      expect(i0.label == parent.label, isTrue);\n    });\n\n    test('reselect_nochange', () {\n      parent.children = <Object>[];\n      i1 = r2.get(parent);\n      expect(i0 == i1, isTrue);\n    });\n\n    test('reselect_change', () {\n      parent.value = 2;\n      i2 = r2.get(parent);\n      expect(i0 == i2, isFalse);\n\n      i3 = r2.get(parent);\n      expect(i2 == i3, isTrue);\n    });\n\n    test('reselect_nochange2', () {\n      parent.children = <Object>[];\n      i3 = r2.get(parent);\n      expect(i2 == i3, isTrue);\n    });\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_middleware/redux_middleware_test.dart",
    "content": "import 'package:test/test.dart';\n\nvoid main() {\n  group('redux_middleware_test', () {\n    /// todo\n  });\n}\n"
  },
  {
    "path": "test/lib/redux_routes/redux_routes_test.dart",
    "content": "import 'package:test/test.dart';\n\nvoid main() {\n  group('redux_routes_test', () {\n    /// todo\n  });\n}\n"
  },
  {
    "path": "test/lib/track.dart",
    "content": "class Track {\n  final List<Pin> _pins = <Pin>[];\n\n  Track();\n\n  factory Track.tags(List<String> tags) {\n    tags ??= <String>[];\n\n    final Track tracer = Track();\n    tags.forEach((String tag) => tracer.append(tag));\n\n    return tracer;\n  }\n\n  factory Track.pins(List<Pin> tags) {\n    tags ??= <Pin>[];\n\n    final Track tracer = Track();\n    tags.forEach((Pin pin) => tracer.append(pin.tag, pin.value));\n\n    return tracer;\n  }\n\n  void append(String tag, [Object value]) {\n    _pins.add(Pin(tag, value));\n  }\n\n  int countOfTag(String tag) =>\n      _pins.fold<int>(0, (count, pin) => pin.tag == tag ? count + 1 : count);\n\n  void remove(String tag) => _pins.retainWhere((pin)=>pin.tag == tag);\n\n  String toString() => _pins\n      .map<String>((node) => node.toString())\n      .fold<String>('', (prev, now) => '$prev\\n=>$now');\n\n  @override\n  bool operator ==(dynamic other) {\n    if (!(other is Track)) return false;\n\n    if (_pins.length != other._pins.length) return false;\n\n    for (int index = 0; index < _pins.length; index++) {\n      if (_pins[index] != other._pins[index]) return false;\n    }\n\n    return true;\n  }\n\n  void reset() {\n    _pins.clear();\n  }\n}\n\nclass Pin {\n  String tag;\n  Object value;\n  DateTime timeStamp;\n\n  Pin(this.tag, [Object value])\n      : timeStamp = DateTime.now(),\n        value = value is Function ? value() : value;\n\n  @override\n  String toString() => '$tag<${value?.toString()}>';\n\n  @override\n  bool operator ==(dynamic other) {\n    if (!(other is Pin)) return false;\n\n    return other.tag == tag && other.value == value;\n  }\n}\n"
  },
  {
    "path": "test/lib/utils/collections_test.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nvoid main() {\n  group('collections_test', () {\n    test('collections_reduce', () {\n      expect(Collections.reduce(null, (int v, int p) => p + v), isNull);\n      expect(\n          Collections.reduce(<int>[1, 2, 3, 4], (int v, int p) => p + v) == 10,\n          isTrue);\n    });\n\n    test('collections_flatten', () {\n      final List<String> a = <String>['a', 'b'];\n      final List<String> b = <String>['1', '2'];\n      final List<List<String>> list = <List<String>>[a, b];\n      final List<String> listFlatten = Collections.flatten(list);\n\n      expect(listFlatten, orderedEquals(<String>['a', 'b', '1', '2']));\n    });\n\n    test('collections_merge', () {\n      final List<String> a = <String>['1', '2'];\n      final List<String> b = <String>['3', '4'];\n      final List<String> merge = Collections.merge(a, b);\n\n      expect(merge, orderedEquals(<String>['1', '2', '3', '4']));\n    });\n\n    test('collections_clone', () {\n      final List<String> list = <String>['hello', 'world'];\n\n      expect(\n          Collections.clone(list), orderedEquals(<String>['hello', 'world']));\n    });\n\n    test('collections_castMapToList', () {\n      final Map<String, String> map = <String, String>{\n        'name': 'John',\n        'gender': 'male',\n        'age': '25'\n      };\n      final List<String> list =\n          Collections.castMapToList(map, (String value, String key) => value);\n      expect(Collections.clone(list),\n          orderedEquals(<String>['John', 'male', '25']));\n    });\n\n    test('collections_isEmpty', () {\n      expect(Collections.isEmpty(null), isTrue);\n\n      expect(Collections.isEmpty(<String>[]), isTrue);\n      expect(Collections.isEmpty(<String>['v']), isFalse);\n      expect(Collections.isNotEmpty(<String>['v']), isTrue);\n\n      expect(Collections.isEmpty(''), isTrue);\n\n      expect(Collections.isEmpty(<String, String>{'name': 'Tom'}), isFalse);\n    });\n  });\n}\n"
  },
  {
    "path": "test/lib/utils/utils_test.dart",
    "content": "import 'package:test/test.dart';\n\nimport 'collections_test.dart' as collections;\n\nvoid main() {\n  group('utils_test', () {\n    collections.main();\n    /// todo\n  });\n}\n"
  },
  {
    "path": "test/pubspec.yaml",
    "content": "name: fish_redux_test\ndescription: A new Flutter package.\nversion: 0.0.1\nauthor:\nhomepage:\n\nenvironment:\n    sdk: '>=2.0.0-dev.68.0 <3.0.0'\n\ndependencies:\n    flutter:\n        sdk: flutter\n    test: ^1.5.1\n    # mockito: ^4.0.0\n\ndev_dependencies:\n    # test: ^1.5.1\n    flutter_test:\n        sdk: flutter\n\n    test_widgets:\n        path: test_widgets\n\n# For information on the generic Dart part of this file, see the\n# following page: https://www.dartlang.org/tools/pub/pubspec\n\n# The following section is specific to Flutter.\nflutter:\n    # To add assets to your package, add an assets section, like this:\n    # assets:\n    #  - images/a_dot_burr.jpeg\n    #  - images/a_dot_ham.jpeg\n    #\n    # For details regarding assets in packages, see\n    # https://flutter.io/assets-and-images/#from-packages\n    #\n    # An image asset can refer to one or more resolution-specific \"variants\", see\n    # https://flutter.io/assets-and-images/#resolution-aware.\n    # To add custom fonts to your package, add a fonts section here,\n    # in this \"flutter\" section. Each entry in this list should have a\n    # \"family\" key with the font family name, and a \"fonts\" key with a\n    # list giving the asset and other descriptors for the font. For\n    # example:\n    # fonts:\n    #   - family: Schyler\n    #     fonts:\n    #       - asset: fonts/Schyler-Regular.ttf\n    #       - asset: fonts/Schyler-Italic.ttf\n    #         style: italic\n    #   - family: Trajan Pro\n    #     fonts:\n    #       - asset: fonts/TrajanPro.ttf\n    #       - asset: fonts/TrajanPro_Bold.ttf\n    #         weight: 700\n    #\n    # For details regarding fonts in packages, see\n    # https://flutter.io/custom-fonts/#from-packages\n"
  },
  {
    "path": "test/test_widgets/.gitignore",
    "content": ".DS_Store\n.dart_tool/\n.packages\n.pub/\nbuild/\n.flutter-plugins\npubspec.lock\n"
  },
  {
    "path": "test/test_widgets/lib/adapter/action.dart",
    "content": "enum ToDoListAction {\n  onAdd,\n  add,\n  onEdit,\n  edit,\n  markDone,\n  remove,\n  onKnowException,\n  onUnKnowException\n}"
  },
  {
    "path": "test/test_widgets/lib/adapter/adapter.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport 'action.dart';\nimport 'state.dart';\n\nWidget toDoView(\n  Todo toDo,\n  Dispatch dispatch,\n  ViewService viewService,\n) {\n  return Container(\n    margin: const EdgeInsets.all(8.0),\n    color: Colors.grey,\n    child: Row(\n      children: <Widget>[\n        Expanded(\n            child: Container(\n          child: Column(\n            children: <Widget>[\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('remove-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 28.0,\n                  color: Colors.yellow,\n                  child: Text(\n                    toDo.title,\n                    style: TextStyle(fontSize: 16.0),\n                  ),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch remove');\n                  dispatch(Action(ToDoListAction.remove, payload: toDo));\n                },\n              ),\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('edit-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 60.0,\n                  color: Colors.grey,\n                  child: Text(toDo.desc, style: TextStyle(fontSize: 14.0)),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch onEdit');\n                  dispatch(Action(ToDoListAction.onEdit, payload: toDo));\n                },\n              )\n            ],\n          ),\n        )),\n        GestureDetector(\n            child: Container(\n              key: ValueKey('mark-${toDo.id}'),\n              color: toDo.isDone ? Colors.green : Colors.red,\n              width: 88.0,\n              height: 88.0,\n              child: Text(toDo.isDone ? 'done' : 'mark\\ndone'),\n              alignment: AlignmentDirectional.center,\n            ),\n            onTap: () {\n              if (!toDo.isDone) {\n                print('dispatch markDone');\n                dispatch(Action(ToDoListAction.markDone, payload: toDo));\n              }\n            },\n            onLongPress: () {\n              print('dispatch Add');\n              dispatch(Action(ToDoListAction.onAdd, payload: toDo));\n            })\n      ],\n    ),\n  );\n}\n\nbool toDoListEffect(Action action, Context<ToDoList> ctx) {\n  if (action.type == ToDoListAction.onAdd) {\n    print('onAdd');\n    ctx.dispatch(Action(ToDoListAction.add, payload: Todo.mock()));\n\n    return true;\n  } else if (action.type == ToDoListAction.onEdit) {\n    print('onEdit');\n    assert(action.payload is Todo);\n\n    Todo toDo = ctx.state.list\n        .firstWhere((i) => i.id == action.payload.id, orElse: () => null)\n        .clone();\n    toDo.desc = '${toDo.desc}-effect';\n    ctx.dispatch(Action(ToDoListAction.edit, payload: toDo));\n    return true;\n  }\n\n  return false;\n}\n\ndynamic toDoListEffectAsync(Action action, Context<ToDoList> ctx) {\n  if (action.type == ToDoListAction.onAdd ||\n      action.type == ToDoListAction.onEdit ||\n      action.type == ToDoListAction.onKnowException ||\n      action.type == ToDoListAction.onUnKnowException) {\n    return Future.delayed(\n        Duration(seconds: 1), () => toDoListEffect(action, ctx));\n  }\n\n  return null;\n}\n\nDispatch toDoListHigherEffect(Context<ToDoList> ctx) =>\n    (Action action) => toDoListEffect(action, ctx);\n\nToDoList toDoListReducer(ToDoList state, Action action) {\n  print('onReduce:${action.type}');\n  if (!(action.payload is Todo)) return state;\n\n  Todo item = action.payload as Todo;\n\n  if (action.type == ToDoListAction.add) {\n    return state.clone()..list.add(item);\n  } else if (action.type == ToDoListAction.markDone) {\n    return state.clone()\n      ..list\n          .firstWhere((toDo) => toDo.id == item.id, orElse: () => null)\n          ?.isDone = true;\n  } else if (action.type == ToDoListAction.remove) {\n    return state.clone()..list.removeWhere((toDo) => toDo.id == item.id);\n  } else if (action.type == ToDoListAction.edit) {\n    Todo toDo = state.list.firstWhere((toDo) => toDo.id == item.id);\n    int index = state.list.indexOf(toDo);\n    toDo = toDo.clone()..desc = item.desc;\n    return state.clone()..list[index] = toDo;\n  } else {\n    return state;\n  }\n}\n\nListAdapter toDoListAdapter(\n  ToDoList state,\n  Dispatch dispatch,\n  ViewService viewService,\n) {\n  return ListAdapter((context, index) {\n    Todo toDo = state.list[index];\n\n    return toDoView(toDo, dispatch, viewService);\n  }, state.list.length);\n}\n"
  },
  {
    "path": "test/test_widgets/lib/adapter/page.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport '../test_base.dart';\nimport 'adapter.dart';\nimport 'state.dart';\n\nWidget pageView(\n  ToDoList state,\n  Dispatch dispatch,\n  ViewService viewService,\n) {\n  print('build pageView');\n  ListAdapter listAdapter = viewService.buildAdapter();\n  return ListView.builder(\n      itemBuilder: listAdapter.itemBuilder, itemCount: listAdapter.itemCount);\n}\n\nconst Map pageInitParams = <String, dynamic>{\n  'list': [\n    <String, dynamic>{\n      'id': '0',\n      'title': 'title-0',\n      'desc': 'desc-0',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '1',\n      'title': 'title-1',\n      'desc': 'desc-1',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '2',\n      'title': 'title-2',\n      'desc': 'desc-2',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '3',\n      'title': 'title-3',\n      'desc': 'desc-3',\n      'isDone': true\n    }\n  ]\n};\n\nToDoList initState(Map map) => ToDoList.fromMap(map);\n\nclass PageWrapper extends StatelessWidget {\n  final Widget child;\n\n  PageWrapper(this.child);\n\n  @override\n  Widget build(BuildContext context) {\n    return child;\n  }\n}\n\nWidget createAdapterWidget(BuildContext context) {\n  return TestPage<ToDoList, Map>(\n          initState: initState,\n          view: pageView,\n          dependencies: Dependencies<ToDoList>(\n              adapter: NoneConn<ToDoList>() +\n                  TestAdapter<ToDoList>(\n                      adapter: toDoListAdapter,\n                      reducer: toDoListReducer,\n                      effect: toDoListEffect)))\n      .buildPage(pageInitParams);\n}\n"
  },
  {
    "path": "test/test_widgets/lib/adapter/state.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nclass Todo implements Cloneable<Todo> {\n  String id;\n  String title;\n  String desc;\n  bool isDone = false;\n\n  Todo();\n\n  factory Todo.mock() => Todo()\n    ..id = 'id-mock'\n    ..title = 'title-mock'\n    ..desc = 'desc-mock'\n    ..isDone = false;\n\n  @override\n  Todo clone() => Todo()\n    ..id = this.id\n    ..title = this.title\n    ..desc = this.desc\n    ..isDone = this.isDone;\n\n  factory Todo.fromMap(Map map) {\n    return Todo()\n      ..id = map['id'] ?? 'uniq'\n      ..title = map['title'] ?? ''\n      ..desc = map['desc'] ?? ''\n      ..isDone = map['isDone'] ?? false;\n  }\n\n  @override\n  bool operator ==(dynamic other) {\n    if (!(other is Todo)) return false;\n\n    return id == other.id &&\n        title == other.title &&\n        desc == other.desc &&\n        isDone == other.isDone;\n  }\n\n  @override\n  String toString() {\n    return 'Todo{id: $id, title: $title, desc: $desc, isDone: $isDone}';\n  }\n}\n\nclass ToDoList implements Cloneable<ToDoList> {\n  List<Todo> list = <Todo>[];\n\n  ToDoList();\n\n  @override\n  ToDoList clone() => ToDoList()..list.addAll(this.list);\n\n  factory ToDoList.fromMap(Map map) {\n    return ToDoList()\n      ..list.addAll(\n          map['list']?.map<Todo>((Map map) => Todo.fromMap(map))?.toList());\n  }\n\n  @override\n  bool operator ==(dynamic other) {\n    if (!(other is ToDoList)) return false;\n\n    if (list.length != other.list.length) return false;\n\n    for (int index = 0; index < list.length; index++) {\n      if (list[index] != other.list[index]) return false;\n    }\n\n    return true;\n  }\n\n  @override\n  String toString() {\n    return '{list: $list}';\n  }\n}\n"
  },
  {
    "path": "test/test_widgets/lib/component/action.dart",
    "content": "enum ToDoListAction {\n  onAdd,\n  add,\n  remove,\n  onBroadcast,\n  broadcast\n}\n\nenum ToDoAction {\n  onEdit,\n  edit,\n  markDone,\n  onBroadcast,\n  broadcast\n}\n"
  },
  {
    "path": "test/test_widgets/lib/component/component.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport 'action.dart';\nimport 'state.dart';\n\nWidget toDoView(Todo toDo, Dispatch dispatch, ViewService viewService) {\n  return Container(\n    margin: const EdgeInsets.all(8.0),\n    color: Colors.grey,\n    child: Row(\n      children: <Widget>[\n        Expanded(\n            child: Container(\n          child: Column(\n            children: <Widget>[\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('remove-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 28.0,\n                  color: Colors.yellow,\n                  child: Text(\n                    toDo.title,\n                    style: TextStyle(fontSize: 16.0),\n                  ),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch remove');\n                  dispatch(\n                      Action(ToDoListAction.remove, payload: toDo.clone()));\n                },\n              ),\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('edit-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 60.0,\n                  color: Colors.grey,\n                  child: Text(toDo.desc, style: TextStyle(fontSize: 14.0)),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch onEdit');\n                  dispatch(Action(ToDoAction.onEdit, payload: toDo.clone()));\n                },\n              )\n            ],\n          ),\n        )),\n        GestureDetector(\n          child: Container(\n            key: ValueKey('mark-${toDo.id}'),\n            color: toDo.isDone ? Colors.green : Colors.red,\n            width: 88.0,\n            height: 88.0,\n            child: Text(toDo.isDone ? 'done' : 'mark\\ndone'),\n            alignment: AlignmentDirectional.center,\n          ),\n          onTap: () {\n            if (!toDo.isDone) {\n              print('dispatch markDone');\n              dispatch(Action(ToDoAction.markDone, payload: toDo.clone()));\n            }\n          },\n          onLongPress: () {\n            print('dispatch broadcast');\n            dispatch(Action(ToDoAction.onBroadcast));\n          },\n        )\n      ],\n    ),\n  );\n}\n\nbool toDoEffect(Action action, Context<Todo> ctx) {\n  if (action.type == ToDoAction.onEdit) {\n    print('onEdit');\n\n    Todo toDo = ctx.state.clone();\n    toDo.desc = '${toDo.desc}-effect';\n\n    ctx.dispatch(Action(ToDoAction.edit, payload: toDo));\n    return true;\n  } else if (action.type == ToDoAction.onBroadcast) {\n    ctx.broadcastEffect(Action(ToDoAction.broadcast));\n    return true;\n  } else if (action.type == Lifecycle.initState) {\n    print('!!! initState ${ctx.state}');\n    return true;\n  } else if (action.type == Lifecycle.dispose) {\n    print('!!! dispose ${ctx.state}');\n    return true;\n  }\n\n  return false;\n}\n\ndynamic toDoEffectAsync(Action action, Context<Todo> ctx) {\n  if (action.type == ToDoAction.onEdit) {\n    return Future.delayed(Duration(seconds: 1), () => toDoEffect(action, ctx));\n  }\n\n  return null;\n}\n\nDispatch toDoHigherEffect(Context<Todo> ctx) =>\n    (Action action) => toDoEffect(action, ctx);\n\nTodo toDoReducer(Todo state, Action action) {\n  if (!(action.payload is Todo) || state.id != action.payload.id) return state;\n\n  print('onReduce:${action.type}');\n\n  if (action.type == ToDoAction.markDone) {\n    return state.clone()..isDone = true;\n  } else if (action.type == ToDoAction.edit) {\n    return state.clone()..desc = action.payload.desc;\n  } else {\n    return state.clone();\n  }\n}\n\nbool shouldUpdate(Todo old, Todo now) => old != now;\n\nbool reducerFilter(Todo toDo, Action action) {\n  return action.type == ToDoAction.edit || action.type == ToDoAction.markDone;\n}\n\nclass ToDoComponent extends Component<Todo> {\n  ToDoComponent()\n      : super(\n            view: toDoView,\n            effect: toDoEffect,\n            reducer: toDoReducer,\n            shouldUpdate: shouldUpdate,\n            filter: reducerFilter);\n}\n\nclass ComponentWrapper extends StatelessWidget {\n  final Widget child;\n\n  ComponentWrapper(this.child);\n\n  @override\n  Widget build(BuildContext context) {\n    return child;\n  }\n}\n"
  },
  {
    "path": "test/test_widgets/lib/component/page.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport '../test_base.dart';\nimport 'action.dart';\nimport 'component.dart';\nimport 'state.dart';\n\nWidget toDoListView(\n  ToDoList state,\n  Dispatch dispatch,\n  ViewService viewService,\n) {\n  print('build toDoListView');\n  return Column(\n    children: <Widget>[\n      Expanded(\n          child: ListView.builder(\n        itemBuilder: (context, index) {\n          if (index == 0) {\n            return viewService.buildComponent('toDo0');\n          } else if (index == 1) {\n            return viewService.buildComponent('toDo1');\n          } else if (index == 2) {\n            return viewService.buildComponent('toDo2');\n          } else if (index == 3) {\n            return viewService.buildComponent('toDo3');\n          } else {\n            Todo toDo = state.list[index];\n            return Container(\n              padding: const EdgeInsets.all(8.0),\n              margin: const EdgeInsets.all(8.0),\n              color: Colors.grey,\n              child: Text(toDo.desc),\n              alignment: AlignmentDirectional.center,\n            );\n          }\n        },\n        itemCount: state.list.length,\n      )),\n      Row(\n        children: <Widget>[\n          Expanded(\n              child: GestureDetector(\n            child: Container(\n              key: ValueKey('Add'),\n              height: 68.0,\n              color: Colors.green,\n              alignment: AlignmentDirectional.center,\n              child: Text('Add'),\n            ),\n            onTap: () {\n              print('dispatch Add');\n              dispatch(Action(ToDoListAction.onAdd));\n            },\n          )),\n        ],\n      )\n    ],\n  );\n}\n\nbool toDoListEffect(Action action, Context<ToDoList> ctx) {\n  if (action.type == ToDoListAction.onAdd) {\n    print('onAdd');\n    ctx.dispatch(Action(ToDoListAction.add, payload: Todo.mock()));\n    return true;\n  } else if (action.type == ToDoListAction.onBroadcast) {\n    ctx.broadcastEffect(Action(ToDoListAction.broadcast));\n    return true;\n  }\n\n  return false;\n}\n\ndynamic toDoListEffectAsync(Action action, Context<ToDoList> ctx) {\n  if (action.type == ToDoListAction.onAdd) {\n    return Future.delayed(\n        Duration(seconds: 1), () => toDoListEffect(action, ctx));\n  }\n\n  return null;\n}\n\nDispatch toDoListHigherEffect(Context<ToDoList> ctx) =>\n    (Action action) => toDoListEffect(action, ctx);\n\nToDoList toDoListReducer(ToDoList state, Action action) {\n  print('onReduce:${action.type}');\n  if (!(action.payload is Todo)) return state;\n\n  if (action.type == ToDoListAction.add) {\n    return state.clone()..list.add(action.payload);\n  } else if (action.type == ToDoListAction.remove) {\n    Todo toDo = state.list.firstWhere((toDo) => toDo.id == action.payload.id);\n    int index = state.list.indexOf(toDo);\n    toDo = toDo.clone()..desc = 'removed';\n    return state.clone()..list[index] = toDo;\n  } else {\n    return state.clone();\n  }\n}\n\nconst Map pageInitParams = <String, dynamic>{\n  'list': [\n    <String, dynamic>{\n      'id': '0',\n      'title': 'title-0',\n      'desc': 'desc-0',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '1',\n      'title': 'title-1',\n      'desc': 'desc-1',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '2',\n      'title': 'title-2',\n      'desc': 'desc-2',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '3',\n      'title': 'title-3',\n      'desc': 'desc-3',\n      'isDone': true\n    }\n  ]\n};\n\nToDoList initState(Map map) => ToDoList.fromMap(map);\n\nclass ToDoComponent0 extends ToDoComponent {}\n\nclass ToDoComponent1 extends ToDoComponent {}\n\nclass ToDoComponent2 extends ToDoComponent {}\n\nclass ToDoComponent3 extends ToDoComponent {}\n\nfinal toDoListDependencies = Dependencies<ToDoList>(slots: {\n  'toDo0': ConnOp<ToDoList, Todo>(\n          get: (toDoList) => toDoList.list[0],\n          set: (toDoList, toDo) => toDoList.list[0] = toDo) +\n      ToDoComponent0(),\n  'toDo1': ConnOp<ToDoList, Todo>(\n          get: (toDoList) => toDoList.list[1],\n          set: (toDoList, toDo) => toDoList.list[1] = toDo) +\n      ToDoComponent1(),\n  'toDo2': ConnOp<ToDoList, Todo>(\n          get: (toDoList) => toDoList.list[2],\n          set: (toDoList, toDo) => toDoList.list[2] = toDo) +\n      ToDoComponent2(),\n  'toDo3': ConnOp<ToDoList, Todo>(\n          get: (toDoList) => toDoList.list[3],\n          set: (toDoList, toDo) => toDoList.list[3] = toDo) +\n      ToDoComponent3(),\n});\n\nWidget createComponentWidget(BuildContext context) {\n  return TestPage<ToDoList, Map>(\n          initState: initState,\n          view: toDoListView,\n          reducer: toDoListReducer,\n          effect: toDoListEffect,\n          dependencies: toDoListDependencies)\n      .buildPage(pageInitParams);\n}\n"
  },
  {
    "path": "test/test_widgets/lib/component/state.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nclass Todo implements Cloneable<Todo> {\n  String id;\n  String title;\n  String desc;\n  bool isDone = false;\n\n  Todo();\n\n  factory Todo.mock() => Todo()\n    ..id = 'id-mock'\n    ..title = 'title-mock'\n    ..desc = 'desc-mock'\n    ..isDone = false;\n\n  @override\n  Todo clone() => Todo()\n    ..id = this.id\n    ..title = this.title\n    ..desc = this.desc\n    ..isDone = this.isDone;\n\n  factory Todo.fromMap(Map map) {\n    return Todo()\n      ..id = map['id'] ?? 'uniq'\n      ..title = map['title'] ?? ''\n      ..desc = map['desc'] ?? ''\n      ..isDone = map['isDone'] ?? false;\n  }\n\n  @override\n  bool operator ==(dynamic other) {\n    if (!(other is Todo)) return false;\n\n    return id == other.id &&\n        title == other.title &&\n        desc == other.desc &&\n        isDone == other.isDone;\n  }\n\n  @override\n  String toString() {\n    return 'Todo{id: $id, title: $title, desc: $desc, isDone: $isDone}';\n  }\n}\n\nclass ToDoList implements Cloneable<ToDoList> {\n  List<Todo> list = <Todo>[];\n\n  ToDoList();\n\n  @override\n  ToDoList clone() => ToDoList()..list.addAll(this.list);\n\n  factory ToDoList.fromMap(Map map) {\n    return ToDoList()\n      ..list.addAll(\n          map['list']?.map<Todo>((Map map) => Todo.fromMap(map))?.toList());\n  }\n\n  @override\n  bool operator ==(dynamic other) {\n    if (!(other is ToDoList)) return false;\n\n    if (list.length != other.list.length) return false;\n\n    for (int index = 0; index < list.length; index++) {\n      if (list[index] != other.list[index]) return false;\n    }\n\n    return true;\n  }\n\n  @override\n  String toString() {\n    return '{list: $list}';\n  }\n}\n"
  },
  {
    "path": "test/test_widgets/lib/dynamic_flow_adapter/action.dart",
    "content": "enum ToDoListAction {\n  onAdd,\n  add,\n  remove,\n  broadcast\n}\n\nenum ToDoAction {\n  onEdit,\n  edit,\n  markDone,\n  onBroadcast,\n  broadcast\n}\n\nenum PageAction {\n  onAdd,\n}\n"
  },
  {
    "path": "test/test_widgets/lib/dynamic_flow_adapter/adapter.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport 'action.dart';\nimport 'state.dart';\n\nWidget toDoView(Todo toDo, Dispatch dispatch, ViewService viewService) {\n  return Container(\n    margin: const EdgeInsets.all(8.0),\n    color: Colors.grey,\n    child: Row(\n      children: <Widget>[\n        Expanded(\n            child: Container(\n          child: Column(\n            children: <Widget>[\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('remove-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 28.0,\n                  color: Colors.yellow,\n                  child: Text(\n                    toDo.title,\n                    style: TextStyle(fontSize: 16.0),\n                  ),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch remove');\n                  dispatch(\n                      Action(ToDoListAction.remove, payload: toDo.clone()));\n                },\n              ),\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('edit-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 60.0,\n                  color: Colors.grey,\n                  child: Text(toDo.desc, style: TextStyle(fontSize: 14.0)),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch onEdit');\n                  dispatch(Action(ToDoAction.onEdit, payload: toDo.clone()));\n                },\n              )\n            ],\n          ),\n        )),\n        GestureDetector(\n          child: Container(\n            key: ValueKey('mark-${toDo.id}'),\n            color: toDo.isDone ? Colors.green : Colors.red,\n            width: 88.0,\n            height: 88.0,\n            child: Text(toDo.isDone ? 'done' : 'mark\\ndone'),\n            alignment: AlignmentDirectional.center,\n          ),\n          onTap: () {\n            if (!toDo.isDone) {\n              print('dispatch markDone');\n              dispatch(Action(ToDoAction.markDone, payload: toDo.clone()));\n            }\n          },\n          onLongPress: () {\n            print('dispatch broadcast');\n            dispatch(Action(ToDoAction.onBroadcast));\n          },\n        )\n      ],\n    ),\n  );\n}\n\nbool toDoEffect(Action action, Context<Todo> ctx) {\n  if (action.type == ToDoAction.onEdit) {\n    print('onEdit');\n\n    Todo toDo = ctx.state.clone();\n    toDo.desc = '${toDo.desc}-effect';\n\n    ctx.dispatch(Action(ToDoAction.edit, payload: toDo));\n    return true;\n  } else if (action.type == ToDoAction.onBroadcast) {\n    ctx.broadcastEffect(Action(ToDoAction.broadcast));\n  } else if (action.type == Lifecycle.initState) {\n    //print('!!! initState ${ctx.state}');\n    return true;\n  } else if (action.type == Lifecycle.dispose) {\n    //print('!!! dispose ${ctx.state}');\n    return true;\n  }\n\n  return false;\n}\n\ndynamic toDoEffectAsync(Action action, Context<Todo> ctx) {\n  if (action.type == ToDoAction.onEdit) {\n    return Future.delayed(Duration(seconds: 1), () => toDoEffect(action, ctx));\n  }\n\n  return null;\n}\n\nDispatch toDoHigherEffect(Context<Todo> ctx) =>\n    (Action action) => toDoEffect(action, ctx);\n\nTodo toDoReducer(Todo state, Action action) {\n  if (!(action.payload is Todo) || state.id != action.payload.id) return state;\n\n  print('onReduce:${action.type}');\n\n  if (action.type == ToDoAction.markDone) {\n    return state.clone()..isDone = true;\n  } else if (action.type == ToDoAction.edit) {\n    return state.clone()..desc = action.payload.desc;\n  } else {\n    return state.clone();\n  }\n}\n\nbool shouldUpdate(Todo old, Todo now) {\n  return old != now;\n}\n\nbool reducerFilter(Todo toDo, Action action) {\n  return action.type == ToDoAction.edit || action.type == ToDoAction.markDone;\n}\n"
  },
  {
    "path": "test/test_widgets/lib/dynamic_flow_adapter/component.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport 'action.dart';\nimport 'state.dart';\n\nWidget toDoView(Todo toDo, Dispatch dispatch, ViewService viewService) {\n  return Container(\n    margin: const EdgeInsets.all(8.0),\n    color: Colors.grey,\n    child: Row(\n      children: <Widget>[\n        Expanded(\n            child: Container(\n          child: Column(\n            children: <Widget>[\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('remove-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 28.0,\n                  color: Colors.yellow,\n                  child: Text(\n                    toDo.title,\n                    style: TextStyle(fontSize: 16.0),\n                  ),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch remove');\n                  dispatch(\n                      Action(ToDoListAction.remove, payload: toDo.clone()));\n                },\n              ),\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('edit-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 60.0,\n                  color: Colors.grey,\n                  child: Text(toDo.desc, style: TextStyle(fontSize: 14.0)),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch onEdit');\n                  dispatch(Action(ToDoAction.onEdit, payload: toDo.clone()));\n                },\n              )\n            ],\n          ),\n        )),\n        GestureDetector(\n          child: Container(\n            key: ValueKey('mark-${toDo.id}'),\n            color: toDo.isDone ? Colors.green : Colors.red,\n            width: 88.0,\n            height: 88.0,\n            child: Text(toDo.isDone ? 'done' : 'mark\\ndone'),\n            alignment: AlignmentDirectional.center,\n          ),\n          onTap: () {\n            if (!toDo.isDone) {\n              print('dispatch markDone');\n              dispatch(Action(ToDoAction.markDone, payload: toDo.clone()));\n            }\n          },\n          onLongPress: () {\n            print('dispatch broadcast');\n            dispatch(Action(ToDoAction.onBroadcast));\n          },\n        )\n      ],\n    ),\n  );\n}\n\nbool toDoEffect(Action action, Context<Todo> ctx) {\n  if (action.type == ToDoAction.onEdit) {\n    print('onEdit');\n\n    Todo toDo = ctx.state.clone();\n    toDo.desc = '${toDo.desc}-effect';\n\n    ctx.dispatch(Action(ToDoAction.edit, payload: toDo));\n    return true;\n  } else if (action.type == ToDoAction.onBroadcast) {\n    ctx.broadcastEffect(Action(ToDoAction.broadcast));\n    return true;\n  } else if (action.type == Lifecycle.initState) {\n    //print('!!! initState ${ctx.state}');\n    return true;\n  } else if (action.type == Lifecycle.dispose) {\n    //print('!!! dispose ${ctx.state}');\n    return true;\n  }\n\n  return false;\n}\n\ndynamic toDoEffectAsync(Action action, Context<Todo> ctx) {\n  if (action.type == ToDoAction.onEdit) {\n    return Future.delayed(Duration(seconds: 1), () => toDoEffect(action, ctx));\n  }\n\n  return null;\n}\n\nDispatch toDoHigherEffect(Context<Todo> ctx) =>\n    (Action action) => toDoEffect(action, ctx);\n\nTodo toDoReducer(Todo state, Action action) {\n  if (!(action.payload is Todo) || state.id != action.payload.id) return state;\n\n  print('onReduce:${action.type}');\n\n  if (action.type == ToDoAction.markDone) {\n    return state.clone()..isDone = true;\n  } else if (action.type == ToDoAction.edit) {\n    return state.clone()..desc = action.payload.desc;\n  } else {\n    return state.clone();\n  }\n}\n\nbool shouldUpdate(Todo old, Todo now) {\n  return old != now;\n}\n\nbool reducerFilter(Todo toDo, Action action) {\n  return action.type == ToDoAction.edit || action.type == ToDoAction.markDone;\n}\n\nclass ToDoComponent extends Component<Todo> {\n  ToDoComponent()\n      : super(\n            view: toDoView,\n            effect: toDoEffect,\n            reducer: toDoReducer,\n            shouldUpdate: shouldUpdate,\n            filter: reducerFilter);\n}\n\nclass ComponentWrapper extends StatelessWidget {\n  final Widget child;\n\n  ComponentWrapper(this.child);\n\n  @override\n  Widget build(BuildContext context) {\n    return child;\n  }\n}\n"
  },
  {
    "path": "test/test_widgets/lib/dynamic_flow_adapter/dynamic_flow_adapter.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nimport '../test_base.dart';\nimport 'action.dart';\nimport 'component.dart';\nimport 'state.dart';\n\nbool toDoListEffect(Action action, Context<ToDoList> ctx) {\n  if (action.type == ToDoListAction.onAdd) {\n    print('adapter onAdd');\n    ctx.dispatch(Action(ToDoListAction.add, payload: Todo.mock()));\n    return true;\n  }\n\n  return false;\n}\n\nToDoList toDoListReducer(ToDoList state, Action action) {\n  print('onReduce:${action.type}');\n  if (!(action.payload is Todo)) return state;\n\n  if (action.type == ToDoListAction.add) {\n    return state.clone()..list.add(action.payload);\n  } else if (action.type == ToDoListAction.remove) {\n    return state.clone()\n      ..list.removeWhere((Todo toDo) => toDo.id == action.payload.id);\n  } else {\n    return state.clone();\n  }\n}\n\nfinal TestDynamicFlowAdapter<ToDoList> testAdapter =\n    TestDynamicFlowAdapter<ToDoList>(\n        pool: <String, ToDoComponent>{'toDo': ToDoComponent()},\n        connector: ConnOp<ToDoList, List<ItemBean>>(\n            get: (ToDoList toDoList) => toDoList.list\n                .map<ItemBean>((Todo toDo) => ItemBean('toDo', toDo))\n                .toList(),\n            set: (ToDoList toDoList, List<ItemBean> beans) {\n              toDoList.list.clear();\n              toDoList.list.addAll(\n                  beans.map<Todo>((ItemBean bean) => bean.data).toList());\n            }),\n        reducer: toDoListReducer,\n        effect: toDoListEffect);\n"
  },
  {
    "path": "test/test_widgets/lib/dynamic_flow_adapter/page.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport '../test_base.dart';\nimport 'action.dart';\nimport 'dynamic_flow_adapter.dart';\nimport 'state.dart';\n\nWidget pageView(\n  ToDoList state,\n  Dispatch dispatch,\n  ViewService viewService,\n) {\n  print('build pageView');\n  final ListAdapter listAdapter = viewService.buildAdapter();\n  return Column(\n    children: <Widget>[\n      Expanded(\n          child: ListView.builder(\n              itemBuilder: listAdapter.itemBuilder,\n              itemCount: listAdapter.itemCount)),\n      Row(\n        children: <Widget>[\n          Expanded(\n              child: GestureDetector(\n            child: Container(\n              key: ValueKey('Add'),\n              height: 68.0,\n              color: Colors.green,\n              alignment: AlignmentDirectional.center,\n              child: Text('Add'),\n            ),\n            onTap: () {\n              print('dispatch Add');\n              dispatch(Action(PageAction.onAdd));\n            },\n            onLongPress: () {\n              print('dispatch broadcast');\n              viewService.broadcastEffect(const Action(ToDoAction.broadcast));\n            },\n          )),\n        ],\n      )\n    ],\n  );\n}\n\nconst Map<String, dynamic> pageInitParams = <String, dynamic>{\n  'list': [\n    <String, dynamic>{\n      'id': '0',\n      'title': 'title-0',\n      'desc': 'desc-0',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '1',\n      'title': 'title-1',\n      'desc': 'desc-1',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '2',\n      'title': 'title-2',\n      'desc': 'desc-2',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '3',\n      'title': 'title-3',\n      'desc': 'desc-3',\n      'isDone': true\n    }\n  ]\n};\n\nbool pageEffect(Action action, Context<ToDoList> ctx) {\n  if (action.type == PageAction.onAdd) {\n    print('page onAdd');\n    ctx.broadcastEffect(Action(ToDoListAction.onAdd, payload: Todo.mock()));\n    return true;\n  }\n\n  return false;\n}\n\nToDoList initState(Map map) => ToDoList.fromMap(map);\n\nWidget createDynamicAdapterWidget(BuildContext context) {\n  return TestPage<ToDoList, Map>(\n          view: pageView,\n          initState: initState,\n          effect: pageEffect,\n          dependencies: Dependencies<ToDoList>(\n              adapter: NoneConn<ToDoList>() + testAdapter))\n      .buildPage(pageInitParams);\n}\n"
  },
  {
    "path": "test/test_widgets/lib/dynamic_flow_adapter/state.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nclass Todo implements Cloneable<Todo> {\n  String id;\n  String title;\n  String desc;\n  bool isDone = false;\n\n  Todo();\n\n  factory Todo.mock() => Todo()\n    ..id = 'id-mock'\n    ..title = 'title-mock'\n    ..desc = 'desc-mock'\n    ..isDone = false;\n\n  @override\n  Todo clone() => Todo()\n    ..id = this.id\n    ..title = this.title\n    ..desc = this.desc\n    ..isDone = this.isDone;\n\n  factory Todo.fromMap(Map map) {\n    return Todo()\n      ..id = map['id'] ?? 'uniq'\n      ..title = map['title'] ?? ''\n      ..desc = map['desc'] ?? ''\n      ..isDone = map['isDone'] ?? false;\n  }\n\n  @override\n  bool operator ==(dynamic other) {\n    if (!(other is Todo)) return false;\n\n    return id == other.id &&\n        title == other.title &&\n        desc == other.desc &&\n        isDone == other.isDone;\n  }\n\n  @override\n  String toString() {\n    return 'Todo{id: $id, title: $title, desc: $desc, isDone: $isDone}';\n  }\n}\n\nclass ToDoList implements Cloneable<ToDoList> {\n  List<Todo> list = <Todo>[];\n\n  ToDoList();\n\n  @override\n  ToDoList clone() => ToDoList()..list.addAll(this.list);\n\n  factory ToDoList.fromMap(Map map) {\n    return ToDoList()\n      ..list.addAll(\n          map['list']?.map<Todo>((Map map) => Todo.fromMap(map))?.toList());\n  }\n\n  @override\n  bool operator ==(dynamic other) {\n    if (!(other is ToDoList)) return false;\n\n    if (list.length != other.list.length) return false;\n\n    for (int index = 0; index < list.length; index++) {\n      if (list[index] != other.list[index]) return false;\n    }\n\n    return true;\n  }\n\n  @override\n  String toString() {\n    return '{list: $list}';\n  }\n}\n"
  },
  {
    "path": "test/test_widgets/lib/main.dart",
    "content": "import 'package:flutter/material.dart' hide Action, Page;\nimport 'package:test_widgets/adapter/page.dart';\nimport 'package:test_widgets/component/page.dart';\nimport 'package:test_widgets/dynamic_flow_adapter/page.dart';\nimport 'package:test_widgets/page/page.dart';\nimport 'package:test_widgets/static_flow_adapter/page.dart';\n\nimport 'test_base.dart';\n\nfinal Map<String, WidgetBuilder> cases = <String, WidgetBuilder>{\n  'buildPage': createPageWidget,\n  'buildComponent': createComponentWidget,\n  'buildAdapter': createAdapterWidget,\n  'buildStaticAdapter': createStaticAdapterWidget,\n  'buildDynamicAdapter': createDynamicAdapterWidget\n};\n\nvoid main() {\n  runApp(TestStub(ListView.builder(\n      itemBuilder: (BuildContext context, int index) {\n        final String name = cases.keys.toList()[index];\n        final WidgetBuilder builder = cases.values.toList()[index];\n\n        return GestureDetector(\n          child: Container(\n            height: 86.0,\n            margin: const EdgeInsets.all(8.0),\n            padding: const EdgeInsets.all(8.0),\n            alignment: AlignmentDirectional.center,\n            color: Colors.grey,\n            child: Text(\n              name,\n              style: const TextStyle(fontSize: 16, color: Colors.black),\n            ),\n          ),\n          onTap: () {\n            Navigator.of(context).push<dynamic>(MaterialPageRoute<dynamic>(\n                builder: (BuildContext context) => TestStub(builder(context))));\n          },\n        );\n      },\n      itemCount: cases.length)));\n}\n"
  },
  {
    "path": "test/test_widgets/lib/page/action.dart",
    "content": "enum ToDoListAction {\n  onAdd,\n  add,\n  onEdit,\n  middlewareEdit,\n  edit,\n  markDone,\n  remove,\n  onKnowException,\n  onUnKnowException\n}"
  },
  {
    "path": "test/test_widgets/lib/page/exception.dart",
    "content": "class KnowException implements Exception{\n  @override\n  bool operator ==(dynamic other) => other is KnowException;\n}\n\nclass UnKnowException implements Exception{\n  @override\n  bool operator ==(dynamic other) => other is UnKnowException;\n}"
  },
  {
    "path": "test/test_widgets/lib/page/page.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport '../test_base.dart';\nimport 'action.dart';\nimport 'exception.dart';\nimport 'state.dart';\n\nWidget toDoView(Todo toDo, BuildContext context, Dispatch dispatch) {\n  return Container(\n    margin: const EdgeInsets.all(8.0),\n    color: Colors.grey,\n    child: Row(\n      children: <Widget>[\n        Expanded(\n            child: Container(\n          child: Column(\n            children: <Widget>[\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('remove-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 28.0,\n                  color: Colors.yellow,\n                  child: Text(\n                    toDo.title,\n                    style: TextStyle(fontSize: 16.0),\n                  ),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch remove');\n                  dispatch(Action(ToDoListAction.remove, payload: toDo));\n                },\n              ),\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('edit-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 60.0,\n                  color: Colors.grey,\n                  child: Text(toDo.desc, style: TextStyle(fontSize: 14.0)),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch onEdit');\n                  dispatch(Action(ToDoListAction.onEdit, payload: toDo));\n                },\n                onLongPress: () {\n                  print('dispatch middlewareEdit');\n                  dispatch(\n                      Action(ToDoListAction.middlewareEdit, payload: toDo));\n                },\n              )\n            ],\n          ),\n        )),\n        GestureDetector(\n          child: Container(\n            key: ValueKey('mark-${toDo.id}'),\n            color: toDo.isDone ? Colors.green : Colors.red,\n            width: 88.0,\n            height: 88.0,\n            child: Text(toDo.isDone ? 'done' : 'mark\\ndone'),\n            alignment: AlignmentDirectional.center,\n          ),\n          onTap: () {\n            if (!toDo.isDone) {\n              print('dispatch markDone');\n              dispatch(Action(ToDoListAction.markDone, payload: toDo));\n            }\n          },\n        )\n      ],\n    ),\n  );\n}\n\nWidget toDoListView(\n  ToDoList state,\n  Dispatch dispatch,\n  ViewService viewService,\n) {\n  print('build toDoListView');\n  return Column(\n    children: <Widget>[\n      Expanded(\n          child: ListView.builder(\n        itemBuilder: (context, index) {\n          Todo toDo = state.list[index];\n          return toDoView(toDo, context, dispatch);\n        },\n        itemCount: state.list.length,\n      )),\n      Row(\n        children: <Widget>[\n          Expanded(\n              child: GestureDetector(\n            child: Container(\n              key: ValueKey('Add'),\n              height: 68.0,\n              color: Colors.green,\n              alignment: AlignmentDirectional.center,\n              child: Text('Add'),\n            ),\n            onTap: () {\n              print('dispatch Add');\n              dispatch(Action(ToDoListAction.onAdd));\n            },\n          )),\n          Expanded(\n              child: GestureDetector(\n                  child: Container(\n                    key: ValueKey('Error'),\n                    height: 68.0,\n                    color: Colors.red,\n                    alignment: AlignmentDirectional.center,\n                    child: Text('Error'),\n                  ),\n                  onTap: () {\n                    print('dispatch KnowException');\n                    dispatch(Action(ToDoListAction.onKnowException));\n                  },\n                  onLongPress: () {\n                    print('dispatch UnKnowException');\n                    dispatch(Action(ToDoListAction.onUnKnowException));\n                  }))\n        ],\n      )\n    ],\n  );\n}\n\nbool toDoListEffect(Action action, Context<ToDoList> ctx) {\n  if (action.type == ToDoListAction.onAdd) {\n    print('onAdd');\n    ctx.dispatch(Action(ToDoListAction.add, payload: Todo.mock()));\n\n    return true;\n  } else if (action.type == ToDoListAction.onEdit) {\n    print('onEdit');\n    assert(action.payload is Todo);\n\n    Todo toDo = ctx.state.list\n        .firstWhere((i) => i.id == action.payload.id, orElse: () => null);\n\n    assert(toDo != null);\n\n    toDo = toDo.clone();\n    toDo.desc = '${toDo.desc}-effect';\n\n    ctx.dispatch(Action(ToDoListAction.edit, payload: toDo));\n    return true;\n  } else if (action.type == ToDoListAction.onKnowException) {\n    throw KnowException();\n  } else if (action.type == ToDoListAction.onUnKnowException) {\n    throw UnKnowException();\n  }\n\n  return false;\n}\n\ndynamic toDoListEffectAsync(Action action, Context<ToDoList> ctx) {\n  if (action.type == ToDoListAction.onAdd ||\n      action.type == ToDoListAction.onEdit ||\n      action.type == ToDoListAction.onKnowException ||\n      action.type == ToDoListAction.onUnKnowException) {\n    return Future.delayed(\n        Duration(seconds: 1), () => toDoListEffect(action, ctx));\n  }\n\n  return null;\n}\n\nToDoList toDoListReducer(ToDoList state, Action action) {\n  print('onReduce:${action.type}');\n  if (!(action.payload is Todo)) return state;\n\n  Todo item = action.payload as Todo;\n\n  if (action.type == ToDoListAction.add) {\n    return state.clone()..list.add(item);\n  } else if (action.type == ToDoListAction.markDone) {\n    return state.clone()\n      ..list\n          .firstWhere((toDo) => toDo.id == item.id, orElse: () => null)\n          ?.isDone = true;\n  } else if (action.type == ToDoListAction.remove) {\n    return state.clone()..list.removeWhere((toDo) => toDo.id == item.id);\n  } else if (action.type == ToDoListAction.edit) {\n    return state.clone()\n      ..list\n          .firstWhere((toDo) => toDo.id == item.id, orElse: () => null)\n          ?.desc = item.desc;\n  } else {\n    return state;\n  }\n}\n\nbool forbidRefreshUI(ToDoList old, ToDoList now) {\n  return false;\n}\n\nbool toDoListErrorHandler(Exception exception, Context<ToDoList> ctx) {\n  print('onErr:$exception');\n  if (exception is KnowException) {\n    return true;\n  }\n\n  return false;\n}\n\nComposable<Dispatch> toDoListMiddleware({\n  Dispatch dispatch,\n  Get<ToDoList> getState,\n}) =>\n    (Dispatch next) => (Action action) {\n          if (action.type == ToDoListAction.middlewareEdit) {\n            assert(action.payload is Todo);\n\n            Todo toDo = getState().list.firstWhere(\n                (i) => i.id == action.payload.id,\n                orElse: () => null);\n\n            assert(toDo != null);\n\n            toDo = toDo.clone();\n            toDo.desc = '${toDo.desc}-middleware';\n\n            dispatch(Action(ToDoListAction.edit, payload: toDo));\n          }\n\n          next(action);\n        };\n\nconst Map pageInitParams = <String, dynamic>{\n  'list': [\n    <String, dynamic>{\n      'id': '0',\n      'title': 'title-0',\n      'desc': 'desc-0',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '1',\n      'title': 'title-1',\n      'desc': 'desc-1',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '2',\n      'title': 'title-2',\n      'desc': 'desc-2',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '3',\n      'title': 'title-3',\n      'desc': 'desc-3',\n      'isDone': true\n    }\n  ]\n};\n\nToDoList initState(Map map) => ToDoList.fromMap(map);\n\nclass PageWrapper extends StatelessWidget {\n  final Widget child;\n\n  PageWrapper(this.child);\n\n  @override\n  Widget build(BuildContext context) {\n    return child;\n  }\n}\n\nWidget createPageWidget(BuildContext context) {\n  return TestPage<ToDoList, Map>(\n      initState: initState,\n      view: toDoListView,\n      reducer: toDoListReducer,\n      effect: toDoListEffectAsync,\n//      shouldUpdate: forbidRefreshWhenAddOrRemove,\n//      onError: toDoListErrorHandler,\n      middleware: [toDoListMiddleware]).buildPage(pageInitParams);\n}\n"
  },
  {
    "path": "test/test_widgets/lib/page/state.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nclass Todo implements Cloneable<Todo> {\n  String id;\n  String title;\n  String desc;\n  bool isDone = false;\n\n  Todo();\n\n  factory Todo.mock() => Todo()\n    ..id = 'id-mock'\n    ..title = 'title-mock'\n    ..desc = 'desc-mock'\n    ..isDone = false;\n\n  @override\n  Todo clone() => Todo()\n    ..id = this.id\n    ..title = this.title\n    ..desc = this.desc\n    ..isDone = this.isDone;\n\n  factory Todo.fromMap(Map map) {\n    return Todo()\n      ..id = map['id'] ?? 'uniq'\n      ..title = map['title'] ?? ''\n      ..desc = map['desc'] ?? ''\n      ..isDone = map['isDone'] ?? false;\n  }\n\n  @override\n  bool operator ==(dynamic other) {\n    if (!(other is Todo)) return false;\n\n    return id == other.id &&\n        title == other.title &&\n        desc == other.desc &&\n        isDone == other.isDone;\n  }\n\n  @override\n  String toString() {\n    return 'Todo{id: $id, title: $title, desc: $desc, isDone: $isDone}';\n  }\n}\n\nclass ToDoList implements Cloneable<ToDoList> {\n  List<Todo> list = <Todo>[];\n\n  ToDoList();\n\n  @override\n  ToDoList clone() => ToDoList()..list.addAll(this.list);\n\n  factory ToDoList.fromMap(Map map) {\n    return ToDoList()\n      ..list.addAll(\n          map['list']?.map<Todo>((Map map) => Todo.fromMap(map))?.toList());\n  }\n\n  @override\n  bool operator ==(dynamic other) {\n    if (!(other is ToDoList)) return false;\n\n    if (list.length != other.list.length) return false;\n\n    for (int index = 0; index < list.length; index++) {\n      if (list[index] != other.list[index]) return false;\n    }\n\n    return true;\n  }\n\n  @override\n  String toString() {\n    return '{list: $list}';\n  }\n}\n"
  },
  {
    "path": "test/test_widgets/lib/source_flow_adapter/action.dart",
    "content": "enum ToDoListAction {\n  onAdd,\n  add,\n  remove,\n  broadcast\n}\n\nenum ToDoAction {\n  onEdit,\n  edit,\n  markDone,\n  onBroadcast,\n  broadcast\n}\n\nenum PageAction {\n  onAdd,\n}\n"
  },
  {
    "path": "test/test_widgets/lib/source_flow_adapter/adapter.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport 'action.dart';\nimport 'state.dart';\n\nWidget toDoView(Todo toDo, Dispatch dispatch, ViewService viewService) {\n  return Container(\n    margin: const EdgeInsets.all(8.0),\n    color: Colors.grey,\n    child: Row(\n      children: <Widget>[\n        Expanded(\n            child: Container(\n          child: Column(\n            children: <Widget>[\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('remove-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 28.0,\n                  color: Colors.yellow,\n                  child: Text(\n                    toDo.title,\n                    style: TextStyle(fontSize: 16.0),\n                  ),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch remove');\n                  dispatch(\n                      Action(ToDoListAction.remove, payload: toDo.clone()));\n                },\n              ),\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('edit-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 60.0,\n                  color: Colors.grey,\n                  child: Text(toDo.desc, style: TextStyle(fontSize: 14.0)),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch onEdit');\n                  dispatch(Action(ToDoAction.onEdit, payload: toDo.clone()));\n                },\n              )\n            ],\n          ),\n        )),\n        GestureDetector(\n          child: Container(\n            key: ValueKey('mark-${toDo.id}'),\n            color: toDo.isDone ? Colors.green : Colors.red,\n            width: 88.0,\n            height: 88.0,\n            child: Text(toDo.isDone ? 'done' : 'mark\\ndone'),\n            alignment: AlignmentDirectional.center,\n          ),\n          onTap: () {\n            if (!toDo.isDone) {\n              print('dispatch markDone');\n              dispatch(Action(ToDoAction.markDone, payload: toDo.clone()));\n            }\n          },\n          onLongPress: () {\n            print('dispatch broadcast');\n            dispatch(Action(ToDoAction.onBroadcast));\n          },\n        )\n      ],\n    ),\n  );\n}\n\nbool toDoEffect(Action action, Context<Todo> ctx) {\n  if (action.type == ToDoAction.onEdit) {\n    print('onEdit');\n\n    Todo toDo = ctx.state.clone();\n    toDo.desc = '${toDo.desc}-effect';\n\n    ctx.dispatch(Action(ToDoAction.edit, payload: toDo));\n    return true;\n  } else if (action.type == ToDoAction.onBroadcast) {\n    ctx.broadcastEffect(Action(ToDoAction.broadcast));\n  } else if (action.type == Lifecycle.initState) {\n    //print('!!! initState ${ctx.state}');\n    return true;\n  } else if (action.type == Lifecycle.dispose) {\n    //print('!!! dispose ${ctx.state}');\n    return true;\n  }\n\n  return false;\n}\n\ndynamic toDoEffectAsync(Action action, Context<Todo> ctx) {\n  if (action.type == ToDoAction.onEdit) {\n    return Future.delayed(Duration(seconds: 1), () => toDoEffect(action, ctx));\n  }\n\n  return null;\n}\n\nDispatch toDoHigherEffect(Context<Todo> ctx) =>\n    (Action action) => toDoEffect(action, ctx);\n\nTodo toDoReducer(Todo state, Action action) {\n  if (!(action.payload is Todo) || state.id != action.payload.id) return state;\n\n  print('onReduce:${action.type}');\n\n  if (action.type == ToDoAction.markDone) {\n    return state.clone()..isDone = true;\n  } else if (action.type == ToDoAction.edit) {\n    return state.clone()..desc = action.payload.desc;\n  } else {\n    return state.clone();\n  }\n}\n\nbool shouldUpdate(Todo old, Todo now) {\n  return old != now;\n}\n\nbool reducerFilter(Todo toDo, Action action) {\n  return action.type == ToDoAction.edit || action.type == ToDoAction.markDone;\n}\n"
  },
  {
    "path": "test/test_widgets/lib/source_flow_adapter/component.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport 'action.dart';\nimport 'state.dart';\n\nWidget toDoView(Todo toDo, Dispatch dispatch, ViewService viewService) {\n  return Container(\n    margin: const EdgeInsets.all(8.0),\n    color: Colors.grey,\n    child: Row(\n      children: <Widget>[\n        Expanded(\n            child: Container(\n          child: Column(\n            children: <Widget>[\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('remove-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 28.0,\n                  color: Colors.yellow,\n                  child: Text(\n                    toDo.title,\n                    style: TextStyle(fontSize: 16.0),\n                  ),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch remove');\n                  dispatch(\n                      Action(ToDoListAction.remove, payload: toDo.clone()));\n                },\n              ),\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('edit-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 60.0,\n                  color: Colors.grey,\n                  child: Text(toDo.desc, style: TextStyle(fontSize: 14.0)),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch onEdit');\n                  dispatch(Action(ToDoAction.onEdit, payload: toDo.clone()));\n                },\n              )\n            ],\n          ),\n        )),\n        GestureDetector(\n          child: Container(\n            key: ValueKey('mark-${toDo.id}'),\n            color: toDo.isDone ? Colors.green : Colors.red,\n            width: 88.0,\n            height: 88.0,\n            child: Text(toDo.isDone ? 'done' : 'mark\\ndone'),\n            alignment: AlignmentDirectional.center,\n          ),\n          onTap: () {\n            if (!toDo.isDone) {\n              print('dispatch markDone');\n              dispatch(Action(ToDoAction.markDone, payload: toDo.clone()));\n            }\n          },\n          onLongPress: () {\n            print('dispatch broadcast');\n            dispatch(Action(ToDoAction.onBroadcast));\n          },\n        )\n      ],\n    ),\n  );\n}\n\nbool toDoEffect(Action action, Context<Todo> ctx) {\n  if (action.type == ToDoAction.onEdit) {\n    print('onEdit');\n\n    Todo toDo = ctx.state.clone();\n    toDo.desc = '${toDo.desc}-effect';\n\n    ctx.dispatch(Action(ToDoAction.edit, payload: toDo));\n    return true;\n  } else if (action.type == ToDoAction.onBroadcast) {\n    ctx.broadcastEffect(Action(ToDoAction.broadcast));\n    return true;\n  } else if (action.type == Lifecycle.initState) {\n    //print('!!! initState ${ctx.state}');\n    return true;\n  } else if (action.type == Lifecycle.dispose) {\n    //print('!!! dispose ${ctx.state}');\n    return true;\n  }\n\n  return false;\n}\n\ndynamic toDoEffectAsync(Action action, Context<Todo> ctx) {\n  if (action.type == ToDoAction.onEdit) {\n    return Future.delayed(Duration(seconds: 1), () => toDoEffect(action, ctx));\n  }\n\n  return null;\n}\n\nDispatch toDoHigherEffect(Context<Todo> ctx) =>\n    (Action action) => toDoEffect(action, ctx);\n\nTodo toDoReducer(Todo state, Action action) {\n  if (!(action.payload is Todo) || state.id != action.payload.id) return state;\n\n  print('onReduce:${action.type}');\n\n  if (action.type == ToDoAction.markDone) {\n    return state.clone()..isDone = true;\n  } else if (action.type == ToDoAction.edit) {\n    return state.clone()..desc = action.payload.desc;\n  } else {\n    return state.clone();\n  }\n}\n\nbool shouldUpdate(Todo old, Todo now) {\n  return old != now;\n}\n\nbool reducerFilter(Todo toDo, Action action) {\n  return action.type == ToDoAction.edit || action.type == ToDoAction.markDone;\n}\n\nclass ToDoComponent extends Component<Todo> {\n  ToDoComponent()\n      : super(\n            view: toDoView,\n            effect: toDoEffect,\n            reducer: toDoReducer,\n            shouldUpdate: shouldUpdate,\n            filter: reducerFilter);\n}\n\nclass ComponentWrapper extends StatelessWidget {\n  final Widget child;\n\n  ComponentWrapper(this.child);\n\n  @override\n  Widget build(BuildContext context) {\n    return child;\n  }\n}\n"
  },
  {
    "path": "test/test_widgets/lib/source_flow_adapter/page.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport '../test_base.dart';\nimport 'action.dart';\nimport 'source_flow_adapter.dart';\nimport 'state.dart';\n\nWidget pageView(\n  ToDoList state,\n  Dispatch dispatch,\n  ViewService viewService,\n) {\n  print('build pageView');\n  final ListAdapter listAdapter = viewService.buildAdapter();\n  return Column(\n    children: <Widget>[\n      Expanded(\n          child: ListView.builder(\n              itemBuilder: listAdapter.itemBuilder,\n              itemCount: listAdapter.itemCount)),\n      Row(\n        children: <Widget>[\n          Expanded(\n              child: GestureDetector(\n            child: Container(\n              key: ValueKey('Add'),\n              height: 68.0,\n              color: Colors.green,\n              alignment: AlignmentDirectional.center,\n              child: Text('Add'),\n            ),\n            onTap: () {\n              print('dispatch Add');\n              dispatch(Action(PageAction.onAdd));\n            },\n            onLongPress: () {\n              print('dispatch broadcast');\n              viewService.broadcastEffect(const Action(ToDoAction.broadcast));\n            },\n          )),\n        ],\n      )\n    ],\n  );\n}\n\nconst Map<String, dynamic> pageInitParams = <String, dynamic>{\n  'list': [\n    <String, dynamic>{\n      'id': '0',\n      'title': 'title-0',\n      'desc': 'desc-0',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '1',\n      'title': 'title-1',\n      'desc': 'desc-1',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '2',\n      'title': 'title-2',\n      'desc': 'desc-2',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '3',\n      'title': 'title-3',\n      'desc': 'desc-3',\n      'isDone': true\n    }\n  ]\n};\n\nbool pageEffect(Action action, Context<ToDoList> ctx) {\n  if (action.type == PageAction.onAdd) {\n    print('page onAdd');\n    ctx.broadcastEffect(Action(ToDoListAction.onAdd, payload: Todo.mock()));\n    return true;\n  }\n\n  return false;\n}\n\nToDoList initState(Map map) => ToDoList.fromMap(map);\n\nWidget createDynamicAdapterWidget(BuildContext context) {\n  return TestPage<ToDoList, Map>(\n          view: pageView,\n          initState: initState,\n          effect: pageEffect,\n          dependencies: Dependencies<ToDoList>(\n              adapter: NoneConn<ToDoList>() + testAdapter))\n      .buildPage(pageInitParams);\n}\n"
  },
  {
    "path": "test/test_widgets/lib/source_flow_adapter/source_flow_adapter.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nimport '../test_base.dart';\nimport 'action.dart';\nimport 'component.dart';\nimport 'state.dart';\n\nbool toDoListEffect(Action action, Context<ToDoList> ctx) {\n  if (action.type == ToDoListAction.onAdd) {\n    print('adapter onAdd');\n    ctx.dispatch(Action(ToDoListAction.add, payload: Todo.mock()));\n    return true;\n  }\n\n  return false;\n}\n\nToDoList toDoListReducer(ToDoList state, Action action) {\n  print('onReduce:${action.type}');\n  if (!(action.payload is Todo)) return state;\n\n  if (action.type == ToDoListAction.add) {\n    return state.clone()..list.add(action.payload);\n  } else if (action.type == ToDoListAction.remove) {\n    return state.clone()\n      ..list.removeWhere((Todo toDo) => toDo.id == action.payload.id);\n  } else {\n    return state.clone();\n  }\n}\n\nfinal TestSourceFlowAdapter<ToDoList> testAdapter =\n    TestSourceFlowAdapter<ToDoList>(\n  pool: <String, ToDoComponent>{'toDo': ToDoComponent()},\n  reducer: toDoListReducer,\n  effect: toDoListEffect,\n);\n"
  },
  {
    "path": "test/test_widgets/lib/source_flow_adapter/state.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nclass Todo implements Cloneable<Todo> {\n  String id;\n  String title;\n  String desc;\n  bool isDone = false;\n\n  Todo();\n\n  factory Todo.mock() => Todo()\n    ..id = 'id-mock'\n    ..title = 'title-mock'\n    ..desc = 'desc-mock'\n    ..isDone = false;\n\n  @override\n  Todo clone() => Todo()\n    ..id = this.id\n    ..title = this.title\n    ..desc = this.desc\n    ..isDone = this.isDone;\n\n  factory Todo.fromMap(Map map) {\n    return Todo()\n      ..id = map['id'] ?? 'uniq'\n      ..title = map['title'] ?? ''\n      ..desc = map['desc'] ?? ''\n      ..isDone = map['isDone'] ?? false;\n  }\n\n  @override\n  bool operator ==(dynamic other) {\n    if (!(other is Todo)) return false;\n\n    return id == other.id &&\n        title == other.title &&\n        desc == other.desc &&\n        isDone == other.isDone;\n  }\n\n  @override\n  String toString() {\n    return 'Todo{id: $id, title: $title, desc: $desc, isDone: $isDone}';\n  }\n}\n\nclass ToDoList extends ItemListLike implements Cloneable<ToDoList> {\n  List<Todo> list = <Todo>[];\n\n  ToDoList();\n\n  @override\n  ToDoList clone() => ToDoList()..list.addAll(this.list);\n\n  factory ToDoList.fromMap(Map map) {\n    return ToDoList()\n      ..list.addAll(\n          map['list']?.map<Todo>((Map map) => Todo.fromMap(map))?.toList());\n  }\n\n  @override\n  bool operator ==(dynamic other) {\n    if (!(other is ToDoList)) return false;\n\n    if (list.length != other.list.length) return false;\n\n    for (int index = 0; index < list.length; index++) {\n      if (list[index] != other.list[index]) return false;\n    }\n\n    return true;\n  }\n\n  @override\n  String toString() {\n    return '{list: $list}';\n  }\n\n  @override\n  Object getItemData(int index) => list[index];\n\n  @override\n  String getItemType(int index) => 'item';\n\n  @override\n  int get itemCount => list?.length ?? 0;\n\n  @override\n  ItemListLike updateItemData(int index, Object data, bool isStateCopied) {\n    list[index] = data;\n    return this;\n  }\n}\n\n// connector: ConnOp<ToDoList, List<ItemBean>>(\n//     get: (ToDoList toDoList) => toDoList.list\n//         .map<ItemBean>((Todo toDo) => noReducer\n//             ? ItemBean('toDoNoReducer', toDo)\n//             : ItemBean('toDo', toDo))\n//         .toList(),\n//     set: (ToDoList toDoList, List<ItemBean> beans) {\n//       toDoList.list.clear();\n//       toDoList.list.addAll(beans\n//           .map<Todo>((ItemBean bean) => bean.data)\n//           .toList());\n//     }),\n"
  },
  {
    "path": "test/test_widgets/lib/static_flow_adapter/action.dart",
    "content": "enum ToDoListAction {\n  onAdd,\n  add,\n  remove,\n}\n\nenum ToDoAction {\n  onEdit,\n  edit,\n  markDone,\n  onBroadcast,\n  broadcast\n}"
  },
  {
    "path": "test/test_widgets/lib/static_flow_adapter/component.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport 'action.dart';\nimport 'state.dart';\n\nWidget toDoView(Todo toDo, Dispatch dispatch, ViewService viewService) {\n  return Container(\n    margin: const EdgeInsets.all(8.0),\n    color: Colors.grey,\n    child: Row(\n      children: <Widget>[\n        Expanded(\n            child: Container(\n          child: Column(\n            children: <Widget>[\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('remove-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 28.0,\n                  color: Colors.yellow,\n                  child: Text(\n                    toDo.title,\n                    style: TextStyle(fontSize: 16.0),\n                  ),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch remove');\n                  dispatch(\n                      Action(ToDoListAction.remove, payload: toDo.clone()));\n                },\n              ),\n              GestureDetector(\n                child: Container(\n                  key: ValueKey('edit-${toDo.id}'),\n                  padding: const EdgeInsets.all(8.0),\n                  height: 60.0,\n                  color: Colors.grey,\n                  child: Text(toDo.desc, style: TextStyle(fontSize: 14.0)),\n                  alignment: AlignmentDirectional.centerStart,\n                ),\n                onTap: () {\n                  print('dispatch onEdit');\n                  dispatch(Action(ToDoAction.onEdit, payload: toDo.clone()));\n                },\n              )\n            ],\n          ),\n        )),\n        GestureDetector(\n          child: Container(\n            key: ValueKey('mark-${toDo.id}'),\n            color: toDo.isDone ? Colors.green : Colors.red,\n            width: 88.0,\n            height: 88.0,\n            child: Text(toDo.isDone ? 'done' : 'mark\\ndone'),\n            alignment: AlignmentDirectional.center,\n          ),\n          onTap: () {\n            if (!toDo.isDone) {\n              print('dispatch markDone');\n              dispatch(Action(ToDoAction.markDone, payload: toDo.clone()));\n            }\n          },\n          onLongPress: () {\n            print('dispatch broadcast');\n            dispatch(Action(ToDoAction.onBroadcast, payload: toDo.clone()));\n          },\n        )\n      ],\n    ),\n  );\n}\n\nbool toDoEffect(Action action, Context<Todo> ctx) {\n  if (action.type == ToDoAction.onEdit) {\n    print('onEdit');\n\n    Todo toDo = ctx.state.clone();\n    toDo.desc = '${toDo.desc}-effect';\n\n    ctx.dispatch(Action(ToDoAction.edit, payload: toDo));\n    return true;\n  } else if (action.type == ToDoAction.onBroadcast) {\n    ctx.broadcastEffect(Action(ToDoAction.broadcast));\n    return true;\n  }\n\n  return false;\n}\n\ndynamic toDoEffectAsync(Action action, Context<Todo> ctx) {\n  if (action.type == ToDoAction.onEdit) {\n    return Future.delayed(Duration(seconds: 1), () => toDoEffect(action, ctx));\n  }\n\n  return null;\n}\n\nDispatch toDoHigherEffect(Context<Todo> ctx) =>\n    (Action action) => toDoEffect(action, ctx);\n\nTodo toDoReducer(Todo state, Action action) {\n  if (!(action.payload is Todo) || state.id != action.payload.id) return state;\n\n  print('onReduce:${action.type}');\n\n  if (action.type == ToDoAction.markDone) {\n    return state.clone()..isDone = true;\n  } else if (action.type == ToDoAction.edit) {\n    return state.clone()..desc = action.payload.desc;\n  } else {\n    return state.clone();\n  }\n}\n\nbool shouldUpdate(Todo old, Todo now) => old != now;\n\nbool reducerFilter(Todo toDo, Action action) {\n  return action.type == ToDoAction.edit || action.type == ToDoAction.markDone;\n}\n\nclass ToDoComponent extends Component<Todo> {\n  ToDoComponent()\n      : super(\n            view: toDoView,\n            effect: toDoEffect,\n            reducer: toDoReducer,\n            shouldUpdate: shouldUpdate,\n            filter: reducerFilter);\n}\n"
  },
  {
    "path": "test/test_widgets/lib/static_flow_adapter/page.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\nimport '../test_base.dart';\nimport 'state.dart';\nimport 'static_flow_adapter.dart';\n\nWidget pageView(\n  ToDoList state,\n  Dispatch dispatch,\n  ViewService viewService,\n) {\n  print('build pageView');\n  ListAdapter listAdapter = viewService.buildAdapter();\n  return ListView.builder(\n      itemBuilder: listAdapter.itemBuilder, itemCount: listAdapter.itemCount);\n}\n\nconst Map pageInitParams = <String, dynamic>{\n  'list': [\n    <String, dynamic>{\n      'id': '0',\n      'title': 'title-0',\n      'desc': 'desc-0',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '1',\n      'title': 'title-1',\n      'desc': 'desc-1',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '2',\n      'title': 'title-2',\n      'desc': 'desc-2',\n      'isDone': false\n    },\n    <String, dynamic>{\n      'id': '3',\n      'title': 'title-3',\n      'desc': 'desc-3',\n      'isDone': true\n    }\n  ]\n};\n\nToDoList initState(Map map) => ToDoList.fromMap(map);\n\nWidget createStaticAdapterWidget(BuildContext context) {\n  return TestPage<ToDoList, Map>(\n          initState: initState,\n          view: pageView,\n          dependencies: Dependencies<ToDoList>(\n              adapter: NoneConn<ToDoList>() + testAdapter))\n      .buildPage(pageInitParams);\n}\n"
  },
  {
    "path": "test/test_widgets/lib/static_flow_adapter/state.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\n\nclass Todo implements Cloneable<Todo> {\n  String id;\n  String title;\n  String desc;\n  bool isDone = false;\n\n  Todo();\n\n  factory Todo.mock() => Todo()\n    ..id = 'id-mock'\n    ..title = 'title-mock'\n    ..desc = 'desc-mock'\n    ..isDone = false;\n\n  @override\n  Todo clone() => Todo()\n    ..id = this.id\n    ..title = this.title\n    ..desc = this.desc\n    ..isDone = this.isDone;\n\n  factory Todo.fromMap(Map map) {\n    return Todo()\n      ..id = map['id'] ?? 'uniq'\n      ..title = map['title'] ?? ''\n      ..desc = map['desc'] ?? ''\n      ..isDone = map['isDone'] ?? false;\n  }\n\n  @override\n  bool operator ==(dynamic other) {\n    if (!(other is Todo)) return false;\n\n    return id == other.id &&\n        title == other.title &&\n        desc == other.desc &&\n        isDone == other.isDone;\n  }\n\n  @override\n  String toString() {\n    return 'Todo{id: $id, title: $title, desc: $desc, isDone: $isDone}';\n  }\n}\n\nclass ToDoList implements Cloneable<ToDoList> {\n  List<Todo> list = <Todo>[];\n\n  ToDoList();\n\n  @override\n  ToDoList clone() => ToDoList()..list.addAll(this.list);\n\n  factory ToDoList.fromMap(Map map) {\n    return ToDoList()\n      ..list.addAll(\n          map['list']?.map<Todo>((Map map) => Todo.fromMap(map))?.toList());\n  }\n\n  @override\n  bool operator ==(dynamic other) {\n    if (!(other is ToDoList)) return false;\n\n    if (list.length != other.list.length) return false;\n\n    for (int index = 0; index < list.length; index++) {\n      if (list[index] != other.list[index]) return false;\n    }\n\n    return true;\n  }\n\n  @override\n  String toString() {\n    return '{list: $list}';\n  }\n}\n"
  },
  {
    "path": "test/test_widgets/lib/static_flow_adapter/static_flow_adapter.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport '../test_base.dart';\nimport 'action.dart';\nimport 'component.dart';\nimport 'state.dart';\n\nbool toDoListEffect(Action action, Context<ToDoList> ctx) {\n  if (action.type == ToDoListAction.onAdd) {\n    print('onAdd');\n    ctx.dispatch(Action(ToDoListAction.add, payload: Todo.mock()));\n\n    return true;\n  }\n\n  return false;\n}\n\ndynamic toDoListEffectAsync(Action action, Context<ToDoList> ctx) {\n  if (action.type == ToDoListAction.onAdd) {\n    return Future.delayed(\n        Duration(seconds: 1), () => toDoListEffect(action, ctx));\n  }\n\n  return null;\n}\n\nDispatch toDoListHigherEffect(Context<ToDoList> ctx) =>\n    (Action action) => toDoListEffect(action, ctx);\n\nToDoList toDoListReducer(ToDoList state, Action action) {\n  print('onReduce:${action.type}');\n  if (!(action.payload is Todo)) return state;\n\n  if (action.type == ToDoListAction.add) {\n    return state.clone()..list.add(action.payload);\n  } else if (action.type == ToDoListAction.remove) {\n    Todo toDo = state.list.firstWhere((toDo) => toDo?.id == action.payload.id);\n    int index = state.list.indexOf(toDo);\n    return state.clone()..list[index] = null;\n  } else {\n    return state.clone();\n  }\n}\n\nclass ToDoComponent0 extends ToDoComponent {}\n\nclass ToDoComponent1 extends ToDoComponent {}\n\nclass ToDoComponent2 extends ToDoComponent {}\n\nclass ToDoComponent3 extends ToDoComponent {}\n\nfinal testAdapter = TestStaticFlowAdapter<ToDoList>(slots: [\n  ConnOp<ToDoList, Todo>(\n          get: (toDoList) => toDoList.list[0],\n          set: (toDoList, toDo) => toDoList.list[0] = toDo) +\n      ToDoComponent0(),\n  ConnOp<ToDoList, Todo>(\n          get: (toDoList) => toDoList.list[1],\n          set: (toDoList, toDo) => toDoList.list[1] = toDo) +\n      ToDoComponent1(),\n  ConnOp<ToDoList, Todo>(\n          get: (toDoList) => toDoList.list[2],\n          set: (toDoList, toDo) => toDoList.list[2] = toDo) +\n      ToDoComponent2(),\n  ConnOp<ToDoList, Todo>(\n          get: (toDoList) => toDoList.list[3],\n          set: (toDoList, toDo) => toDoList.list[3] = toDo) +\n      ToDoComponent3()\n], reducer: toDoListReducer, effect: toDoListEffect);\n"
  },
  {
    "path": "test/test_widgets/lib/test_base.dart",
    "content": "import 'package:fish_redux/fish_redux.dart';\nimport 'package:flutter/material.dart' hide Action, Page;\n\n@immutable\nclass TestStub extends StatefulWidget {\n  final Widget testWidget;\n  final String title;\n\n  const TestStub(this.testWidget, {this.title = 'FlutterTest'});\n\n  @override\n  _StubState createState() => _StubState();\n}\n\nclass _StubState extends State<TestStub> {\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n        title: widget.title,\n        home: Scaffold(\n            appBar: AppBar(title: Text(widget.title)),\n            body: widget.testWidget));\n  }\n}\n\nclass TestPage<T extends Cloneable<T>, P> extends Page<T, P> {\n  TestPage({\n    @required InitState<T, P> initState,\n    List<Middleware<T>> middleware,\n    @required ViewBuilder<T> view,\n    Reducer<T> reducer,\n    ReducerFilter<T> filter,\n    Effect<T> effect,\n    Dependencies<T> dependencies,\n    ShouldUpdate<T> shouldUpdate,\n    WidgetWrapper wrapper,\n    Key Function(T) key,\n  }) : super(\n          initState: initState,\n          middleware: middleware,\n          view: view,\n          reducer: reducer,\n          filter: filter,\n          effect: effect,\n          dependencies: dependencies,\n          shouldUpdate: shouldUpdate,\n          wrapper: wrapper,\n          key: key,\n        );\n}\n\nclass TestComponent<T extends Cloneable<T>> extends Component<T> {\n  TestComponent({\n    @required ViewBuilder<T> view,\n    Reducer<T> reducer,\n    ReducerFilter<T> filter,\n    Effect<T> effect,\n    Dependencies<T> dependencies,\n    ShouldUpdate<T> shouldUpdate,\n    WidgetWrapper wrapper,\n    Key Function(T) key,\n  }) : super(\n            view: view,\n            reducer: reducer,\n            filter: filter,\n            effect: effect,\n            dependencies: dependencies,\n            shouldUpdate: shouldUpdate,\n            wrapper: wrapper,\n            key: key);\n}\n\nclass TestAdapter<T extends Cloneable<T>> extends Adapter<T> {\n  TestAdapter({\n    AdapterBuilder<T> adapter,\n    Reducer<T> reducer,\n    Effect<T> effect,\n    ReducerFilter<T> filter,\n    Dependencies<T> dependencies,\n  }) : super(\n            adapter: adapter,\n            reducer: reducer,\n            effect: effect,\n            filter: filter,\n            dependencies: dependencies);\n}\n\nclass TestStaticFlowAdapter<T extends Cloneable<T>>\n    extends StaticFlowAdapter<T> {\n  TestStaticFlowAdapter({\n    @required List<Dependent<T>> slots,\n    Reducer<T> reducer,\n    Effect<T> effect,\n    ReducerFilter<T> filter,\n  }) : super(slots: slots, reducer: reducer, effect: effect, filter: filter);\n}\n\nclass TestDynamicFlowAdapter<T extends Cloneable<T>>\n    extends DynamicFlowAdapter<T> {\n  TestDynamicFlowAdapter({\n    @required Map<String, AbstractLogic<Object>> pool,\n    @required ConnOp<T, List<ItemBean>> connector,\n    ReducerFilter<T> filter,\n    Reducer<T> reducer,\n    Effect<T> effect,\n  }) : super(\n            pool: pool,\n            connector: connector,\n            reducer: reducer,\n            effect: effect,\n            filter: filter);\n}\n\nclass TestSourceFlowAdapter<T extends AdapterSource>\n    extends SourceFlowAdapter<T> {\n  TestSourceFlowAdapter({\n    @required Map<String, AbstractLogic<Object>> pool,\n    ReducerFilter<T> filter,\n    Reducer<T> reducer,\n    Effect<T> effect,\n  }) : super(\n          pool: pool,\n          reducer: reducer,\n          effect: effect,\n          filter: filter,\n        );\n}\n"
  },
  {
    "path": "test/test_widgets/pubspec.yaml",
    "content": "name: test_widgets\ndescription: widgets for redux test...\n\nenvironment:\n    sdk: '>=2.0.0-dev.28.0 <3.0.0'\n\ndependencies:\n    flutter:\n        sdk: flutter\n    fish_redux:\n        path: ../../\n\ndev_dependencies:\n    flutter_driver:\n        sdk: flutter\n    flutter_test:\n        sdk: flutter\n"
  }
]