{"id":37021882,"url":"https://github.com/inkremental/inkremental","last_synced_at":"2026-01-14T02:36:15.994Z","repository":{"id":37796129,"uuid":"205363267","full_name":"inkremental/inkremental","owner":"inkremental","description":"Easy-to-use Kotlin DSL for building Android UI using incremental DOM","archived":true,"fork":true,"pushed_at":"2022-12-12T18:22:29.000Z","size":2155,"stargazers_count":58,"open_issues_count":0,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2023-07-03T14:11:20.382Z","etag":null,"topics":["android","dsl","incremental-dom","kotlin","view"],"latest_commit_sha":null,"homepage":"https://inkremental.dev/","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"anvil-ui/anvil","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/inkremental.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"docs/contributing.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-08-30T10:51:56.000Z","updated_at":"2023-05-21T13:08:39.000Z","dependencies_parsed_at":"2023-01-27T23:31:45.980Z","dependency_job_id":null,"html_url":"https://github.com/inkremental/inkremental","commit_stats":null,"previous_names":["inkremental/anvil"],"tags_count":28,"template":null,"template_full_name":null,"purl":"pkg:github/inkremental/inkremental","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inkremental%2Finkremental","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inkremental%2Finkremental/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inkremental%2Finkremental/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inkremental%2Finkremental/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/inkremental","download_url":"https://codeload.github.com/inkremental/inkremental/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inkremental%2Finkremental/sbom","scorecard":{"id":488995,"data":{"date":"2025-08-11","repo":{"name":"github.com/inkremental/inkremental","commit":"5151ad62ced0658a0265f7eed9564152634afa9a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":7,"reason":"Found 7/10 approved changesets -- score normalized to 7","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"project is archived","details":["Warn: Repository is archived."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/push-build.yml:1","Warn: no topLevel permission defined: .github/workflows/push-publish.yml:1","Warn: no topLevel permission defined: .github/workflows/site.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/push-build.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/inkremental/inkremental/push-build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/push-build.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/inkremental/inkremental/push-build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/push-build.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/inkremental/inkremental/push-build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/push-build.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/inkremental/inkremental/push-build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/push-build.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/inkremental/inkremental/push-build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/push-publish.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/inkremental/inkremental/push-publish.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/push-publish.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/inkremental/inkremental/push-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/push-publish.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/inkremental/inkremental/push-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/push-publish.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/inkremental/inkremental/push-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/push-publish.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/inkremental/inkremental/push-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/site.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/inkremental/inkremental/site.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/site.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/inkremental/inkremental/site.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/site.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/inkremental/inkremental/site.yml/master?enable=pin","Info:   0 out of  10 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Binary-Artifacts","score":8,"reason":"binaries present in source code","details":["Warn: binary detected: gradle/wrapper/gradle-wrapper.jar:1","Warn: binary detected: meta/gradle/wrapper/gradle-wrapper.jar:1"],"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact 0.14 not signed: https://api.github.com/repos/inkremental/inkremental/releases/30126727","Warn: release artifact 0.13 not signed: https://api.github.com/repos/inkremental/inkremental/releases/29601519","Warn: release artifact support-v4_1.12 not signed: https://api.github.com/repos/inkremental/inkremental/releases/29600824","Warn: release artifact v0.12_core not signed: https://api.github.com/repos/inkremental/inkremental/releases/27598408","Warn: release artifact v0.11_core not signed: https://api.github.com/repos/inkremental/inkremental/releases/24729686","Warn: release artifact 0.14 does not have provenance: https://api.github.com/repos/inkremental/inkremental/releases/30126727","Warn: release artifact 0.13 does not have provenance: https://api.github.com/repos/inkremental/inkremental/releases/29601519","Warn: release artifact support-v4_1.12 does not have provenance: https://api.github.com/repos/inkremental/inkremental/releases/29600824","Warn: release artifact v0.12_core does not have provenance: https://api.github.com/repos/inkremental/inkremental/releases/27598408","Warn: release artifact v0.11_core does not have provenance: https://api.github.com/repos/inkremental/inkremental/releases/24729686"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"11 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-jxhc-q857-3j6g","Warn: Project is vulnerable to: GHSA-52p9-v744-mwjj","Warn: Project is vulnerable to: GHSA-mqm2-cgpr-p4m6","Warn: Project is vulnerable to: GHSA-2rxp-v6pw-ch6m","Warn: Project is vulnerable to: GHSA-4xqq-m2hx-25v8","Warn: Project is vulnerable to: GHSA-5866-49gr-22v4","Warn: Project is vulnerable to: GHSA-8cr8-4vfw-mr7h","Warn: Project is vulnerable to: GHSA-r55c-59qm-vjw6","Warn: Project is vulnerable to: GHSA-vg3r-rm7w-2xgh","Warn: Project is vulnerable to: GHSA-vmwr-mc7x-5vc3","Warn: Project is vulnerable to: GHSA-5cm2-9h8c-rvfx"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 27 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-19T18:34:08.592Z","repository_id":37796129,"created_at":"2025-08-19T18:34:08.592Z","updated_at":"2025-08-19T18:34:08.592Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408711,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","dsl","incremental-dom","kotlin","view"],"created_at":"2026-01-14T02:36:15.280Z","updated_at":"2026-01-14T02:36:15.986Z","avatar_url":"https://github.com/inkremental.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Inkremental - reactive views for Android\n\n## Project status\n\nThe library is not being maintained. [Jetpack Compose](https://developer.android.com/jetpack/compose) is a main way to do concise declarative UI in Android, and [Compose Multiplatform](https://www.jetbrains.com/lp/compose-mpp/) by JetBrains extends it even further by covering Desktop, Web, and at some point iOS. If you need any suggestions on migration, you can hit us via Telegram at [@android_declarative](https://t.me/android_declarative).\n\n\u003cbr/\u003e\n\n[![Build Status](https://travis-ci.org/inkremental/anvil.svg?branch=master)](https://travis-ci.org/inkremental/anvil)\n\n\u003cbr/\u003e\n\n\u003cdiv\u003e\n\u003cimg align=\"left\" src=\"https://raw.githubusercontent.com/inkremental/anvil/master/logo/ic_launcher.png\" alt=\"logo\" width=\"96px\" height=\"96px\" /\u003e\n\u003cp\u003e\nInkremental is a small library for creating reactive user interfaces. Originally inspired by \u003ca href=\"https://facebook.github.io/react/\"\u003eReact\u003c/a\u003e, it suits well as a view layer for \u003ca href=\"https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel\"\u003eMVVM\u003c/a\u003e or \u003ca href=\"http://redux.js.org/\"\u003eRedux\u003c/a\u003e design patterns.\nStarted as a fork of Java library Anvil, it is fully rewriten in Kotlin, with many improvements pulled in and more to come!\n\u003c/p\u003e\n\u003c/div\u003e\n\n\u003cbr/\u003e\n\n## Features\n\n* Super small (4 hand-written classes + 1 generated class)\n* Easy to learn (top-level API is only 5 functions)\n* Fast (uses no reflection¹)\n* Efficient (views are updated lazily, if the data change didn't affect the view - it remains untouched)\n* Easy to read declarative syntax\n* Powerfull typesafe dsl\n* XML layouts are supported, too\n\n¹Reflection is still used to inflate views once (standard XML inflater does the\nsame thing, so no performance loss here).\n\n## Installation\n\nWith Kotlin Gradle DSL\n```kotlin\nrepositories {\n    mavenCentral()\n}\ndependencies {\n    //For Lollipop as min SDK target \n    implementation(\"dev.inkremental:anvil-sdk:21-$latestReleaseVersion\")\n    //For Kitkat as min SDK target\n    //implementation(\"dev.inkremental:anvil-sdk:19-$latestReleaseVersion\") \n    //For ICS as min SDK target\n    //implementation(\"dev.inkremental:anvil-sdk:17-$latestReleaseVersion\") \n    implementation(\"dev.inkremental:anvil-support-v4:1.1.0-$latestReleaseVersion\") \n    implementation(\"dev.inkremental:anvil-appcompat-v7:1.1.0-$latestReleaseVersion\")\n    implementation(\"dev.inkremental:anvil-recyclerview-v7:1.1.0-$latestReleaseVersion\")\n    implementation(\"dev.inkremental:anvil-design:1.1.0-$latestReleaseVersion\")\n    implementation(\"com.google.android.material:material:1.1.0\") //at the moment you should also declare original dependency\n    implementation(\"dev.inkremental:anvil-cardview-v7:1.0.0-$latestReleaseVersion\")\n    implementation(\"dev.inkremental:anvil-constraintlayout:1.1.3-$latestReleaseVersion\")       \n    implementation(\"androidx.constraintlayout:constraintlayout:1.1.3\") //at the moment you should also declare original dependency\n}\n```\n Or with Groovy Gradle DSL\n ```groovy\n repositories {\n    mavenCentral()\n }\n dependencies {\n     //For Lollipop as min SDK target\n     implementation \"dev.inkremental:anvil-sdk:21-$latestReleaseVersion\"\n     //For Kitkat as min SDK target\n     //implementation \"dev.inkremental:anvil-sdk:19-$latestReleaseVersion\"\n     //For ICS as min SDK target\n     //implementation \"dev.inkremental:anvil-sdk:17-$latestReleaseVersion\"\n     implementation \"dev.inkremental:anvil-support-v4:1.1.0-$latestReleaseVersion\" \n     implementation \"dev.inkremental:anvil-appcompat-v7:1.1.0-$latestReleaseVersion\"\n     implementation \"dev.inkremental:anvil-recyclerview-v7:1.1.0-$latestReleaseVersion\"\n     implementation \"dev.inkremental:anvil-design:1.1.0-$latestReleaseVersion\"\n     implementation \"com.google.android.material:material:1.1.0\" //at the moment you should also declare original dependency\n     implementation \"dev.inkremental:anvil-cardview-v7:1.0.0-$latestReleaseVersion\"\n     implementation \"dev.inkremental:anvil-constraintlayout:1.1.3-$latestReleaseVersion\"       \n     implementation \"androidx.constraintlayout:constraintlayout:1.1.3\" //at the moment you should also declare original dependency\n }\n ```\nInkremental comes in multiple builds for different minimal SDK versions:\n\n* anvil-sdk17 (ICS)\n* anvil-sdk19 (Kitkat)\n* anvil-sdk21 (Lollipop)\n\nOther API levels are not added because they had very few UI-related methods added.\n\nNote that minimal supported SDK level is **subject to change in future**. Let us know\nif you are still using API levels 17 or 18!\n\n## Examples\n\nNormally you would write your layouts in XMLs, then get your views by their\nIDs and set their listeners, finally you would observe your data and modify\nview properties as your data changes.\n\nInkremental simplifies most of this boring routine.\n\nDeclare your layout, assign event listeners and bind data:\n\n```Kotlin\nvar ticktock = 0\noverride fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        \n        renderableContentView {\n            linearLayout {\n                size(MATCH, MATCH)\n                padding(8.dp)\n                orientation(LinearLayout.VERTICAL)\n\n                textView {\n                    size(MATCH, WRAP)\n                    text(\"Tick-tock: $ticktock\")\n                }\n\n                button {\n                    size(MATCH, WRAP)\n                    text(\"Close\")\n                    // Finish current activity when the button is clicked\n                    onClick { v -\u003e \n                        finish()\n                    }\n                }\n            }\n        }\n}\n```\n\nHere we've created a very simple reactive view and added it to our Activity.\nWe've declared our layout (a LinearLayout with a TextView inside). We've defined\nstyles (width, height, orientation and padding). We've set OnClickListener to \nthe button. We've also bound a variable \"ticktock\" to the text property inside a TextView.\n\nNext, let's update your views as your data changes:\n\n```Kotlin\nticktock++\nInkremental.render()\n```\n\nAt this point your layout will be updated and the TextView will contain text\n\"Tick-Tock: 1\" instead of \"Tick-Tock: 0\". However the only actual view method\nbeing called would be setText().\n\nYou should know that there is no need to call Inkremental.render() inside your event\nlisteners, it's already triggered after each UI listener call:\n\n``` Kotlin\nfun view() {\n    linearLayout {\n        textView {\n            text(\"Clicks: $numClicks\")\n        }\n        button {\n            text(\"Click me\")\n            onClick {\n                numClicks++ // text view will be updated automatically\n            }\n        }\n    }\n}\n```\n\n## Made with Inkremental/Anvil\n\n[![MacGeiz](https://lh3.googleusercontent.com/M6NheR2ztNUypiiu2tnUFQ0AAJTMKlUFyDhdNzFEYKzHjOIapcNySZlgF67pbDSxXOY=s96)](https://play.google.com/store/apps/details?id=com.macgeiz.app)\n[![Talalarmo](https://raw.githubusercontent.com/trikita/talalarmo/master/src/main/res/drawable-xhdpi/ic_launcher.png)](https://github.com/trikita/talalarmo)\n[![Slide](https://raw.githubusercontent.com/trikita/slide/master/src/main/res/mipmap-xxhdpi/ic_launcher.png)](https://github.com/trikita/slide)\n\n## How it works\n\nNo magic. When a renderable object is being constructed there are 3 types of\noperations: push view, modify some attribute of the current view, and \npop view. If you're familiar with\n[incremental DOM](http://google.github.io/incremental-dom/#about) - Inkremental\nfollows the same approach.\n\nPushing a view adds it as a child to the parent view from the top of\nthe stack. Attribute modification simply sets the given property to\nthe current view on top of the stack. Pop unwinds the stack.\n\nWhen you mount this layout (assuming the `name` is \"John\"):\n\n```Kotlin\nlinearLayout {\n    textView {\n        text(\"Hello $name\")\n    }\n}\n```\n\nIt does the following sequence of actions:\n\n```\nPush LinearLayout (adding it to the root view)\nPush TextView (adding it to the linear layout)\nAttr text \"Hello John\" (calling setText of the TextView)\nPop\nPop\n```\n\nThe only trick is that these actions are cached into a so called \"virtual layout\" -\na tree-like structure matching the actual layout of views and their properties.\n\nSo when you call `Inkremental.render()` next time it compares the sequence of\nactions with the cache and skips property values if they haven't change. Which means on the next\n`Inkremental.render()` call _the views will remain untouched. This caching technique\nmakes render a very quick operation (having a layout of 100 views, 10\nattributes each you can do about 4000 render cycles per second!).\n\nNow, if you modify the `name` from \"John\" to \"Jane\" and call Inkremental.render() it will do the following:\n\n```\nPush LinearLayout (noop)\nPush TextView (noop)\nAttr text \"Hello Jane\" (comparing with \"Hello John\" from the pervious render,\n  noticing the difference and calling setText(\"Hello Jane\") to the TextView)\nPop\nPop\n```\n\nSo if you modify one of the variables \"bound\" to some of the attributes - the\ncache will be missed and attribute will be updated.\n\nFor all event listeners a \"proxy\" is generated, which delegates its\nmethod calls to your actual event listener, but calls `Inkremental.render()` after each\nmethod. This is useful, because most of your data models are modified when\nthe user interacts with the UI, so you write less code without calling\n`Inkremental.render()` from every listener. Remember, no-op renders are very fast.\n\n## Supported languages and API levels\n\nInkremental library contains only a few classes to work with the virtual layout, but most\nof the DSL (domain-specific language describing how to create views/layouts and set\ntheir attributes) is generated from `android.jar`, Jetpack and third-party libraries.\n\n## API\n\nHere's a list of classes and methods you need to know to work with Inkremental like a pro:\n\n* `Inkremental.Renderable` - functional interface that one should implement to\n    describe layout structure, style and data bindings.\n\n* `Inkremental.mount(View, () -\u003e Unit)` - mounts renderable layout into a\n    View or a ViewGroup\n\n* `Inkremental.unmount(View)` - unmounts renderable layout from the View\n    removing all its child views if it's a ViewGroup\n\n* `Inkremental.render()` - starts a new render cycle updating all mounted views\n\n* `Inkremental.currentView(): View` - returns the view which is currently being\n    rendered. Useful in some very rare cases and only inside the Renderable's\n    method `view()` to get access to the real view and modifying it manually.\n\n* `RenderableView` - a most typical implementation of Inkremental.Renderable.\n    Extending this class and overriding its method `view()` allows to create\n    self-contained reactive components that are mounted and unmounted\n    automatically.\n\n* `RenderableAdapter` - extending this class and overriding its `getCount()`,\n    `getItem(int)` and `view(int)` allows to create lists where each item is a\n    standalone reactive renderable object.\n\n* `RenderableAdapter.withItems(list, cb(index, item))` - a shortcut to create\n    simple adapters for the given list of items. `cb` is a lambda that describes\n    the layout and bindings of a certain list item at the given index.\n\n## DSL\n\nThe bottom part of the iceberg is Inkremental DSL.\n\nDSL consists of a few handwritten property setters, but most of it is\ngenerated from java classes in the android SDK.\n\nSee a full list of the DSL methods for each API level [here](https://github.com/inkremental/inkremental/blob/master/DSL.md).\n\n### Property setters\n\nThe setters are named as the view methods from Android SDK, but without the\n\"set\" prefix. E.g. \"text(s)\" instead of \"setText(s)\", \"backgroundDrawable(d)\"\ninstead of \"setBackgroundDrawable(d)\" and so on.\n\n### Event listeners\n\nEvent listeners also have names from the Android SDK, but without the \"set\"\nprefix and the \"Listener\" suffix, e.g. \"onClick\" instead of \"setOnClickListener\".\n\n### Handwritten bindings\n\nFor LayoutParams the bindings can't be generated easily, so it was faster to write them manually:\n\n* `size(width, height)` - set width and height. Special constants like `WRAP`, `FILL`\n  and `MATCH` are available.\n* `dip(x)` - returns the value in pixels for the dimension in density-independent\n  pixels. Often used with size, padding or margin properties.\n* `margin(m)`, `margin(h, v)`, `margin(l, t, r, b)` - set view margin for all 4\n  sides, for horizontal/vertical dimensions or for each side individually.\n* `weight(w)` - modifies view layout weight.\n* `layoutGravity(g)` - modifies layout gravity of a view. Common constants like\n  `START`, `END`, `CENTER`, `CENTER_VERTICAL`, etc. are available.\n* `align(verb, anchor)`, `align(verb)` - base functions for relative layout params.\n* `above(id)`, `alignBaseline(id)`, `alignBottom(id)`, `alignEnd(id)`, `alignLeft(id)`, `alignParentBottom()`,\n  `alignParentEnd()`, `alignParentLeft()`, `alignParentRight()`, `alignParentStart()`, `alignParentTop()`,\n  `alignRight(id)`, `alignTop(id)`, `below(id)`, `centerHorizontal()`, `centerVertical()`, `centerInParent()`,\n  `toEndOf(id)`, `toLeftOf(id)`, `toRightOf(id)`, `toStartOf(id)` - all possible settings for relative layout params\n\nA few bindings have been  written for other use cases which we find useful:\n\n* `init(Runnable)` - executes a runnable once, useful to initialize custom views (see also `Inkremental.currentView()`).\n* `R()` - returns a `Resources` object associated with the current view. Useful for\n  multiple screen support (sizes, dpi, orientation etc).\n* `isPortrait()` - returns true if a screen is portrait-oriented. Useful for\n    tweaking layouts for different orientations.\n* `typeface(font)` - loads font from assets by its file name and sets the typeface to\n    a TextView\n* `padding(p)`, `padding(h, v)`, `padding(l, t, r, b)` - set view padding for all 4\n    sides, for horizontal/vertical dimensions or for each side individually.\n* `visibility(flag)` - sets the visibility property to `View.VISIBLE` or `View.GONE` depending on\n    the `flag` boolean value\n* `shadowLayer(radius, dx, dy, color)` - sets shadow layer of a TextView\n* `onTextChanged(textWatcher)` - binds a text watcher to an `EditText`. No\n    `Inkremental.render()` is called in this case, because you're likely to get an infinite\n    recursion.\n* `text(StringBuilder)` - binds a string builder to the edit text, so when you\n    change its contents - the edit text is changed, and if you type something\n    manually - the string builder gets modified. So far it's the only two-way\n    data binding, becayse TextWatcher is a complicated beast.\n* `onItemSelected(lambda)` - accepts a functional interface to handle a `Spinner`\n    events. `onNothingSelected()` method is omitted, because it's rarely used anyway.\n\nIf a binding you need is not in the list - please, check [corresponding issue](https://github.com/Inkremental/anvil/issues/24) and report it there.\n\nA special case for animations is added:\n\n* `anim(trigger, Animator)` - starts animation when `trigger` is true, cancels it\n    when the `trigger` becomes false.\n* `anim(trigger, AnimatorIn, AnimatorOut)` - starts AnimatorIn when `trigger` is true, starts AnimatorOut \n    when the `trigger` becomes false.    \n\nFinally, a few low-level DSL functions are there, which you would no need unless you want to write your own property setters or custom view builders:\n\n* `v(class, attrs...)` - pushes view, applies attributes, doesn't pop the view.\n* `v(class, renderable)` - pushes the view, applies the renderable to fulfil\n    attributes and child views, pops the view. \n* `attr(func, value)` - checks the cache for the given value of the given\n    property setter function. Often used to create your own property setter\n    binding.\n\n## XML layouts\n\nIf you're migrating an existing project to Inkremental or if you prefer to keep your\nview layouts declared in XML - you can do so:\n\n``` kotlin\npublic override fun view() {\n    // A designer gave us an XML with some fancy layout:\n    // a viewgroup with a button and a progress bar in it\n    xml(R.layout.my_layout) {\n        backgroundColor(Settings.bgColor) // will modify root layout view color\n\n        withId(R.id.my_button) {\n            // button state may depend on some variable\n            enabled(isMyButtonEnabled)\n                // button click listener can be attached\n                onClick { v -\u003e\n                    ...\n                }\n        }\n\n        withId(R.id.my_progress_bar) {\n            visible(isMyProgressBarShown)\n            progress(someProgressValue)\n        }\n    }\n}\n```\n\nHere `xml()` creates a view node, much like `frameLayout()` or `textView()`,\nexcept for it uses an XML file to inflate the views, not the direct view class\nconstructor. Much like view \"builders\", `xml()` takes a renderable lambda as a\nparameter and uses that to modify the created view.\n\nAny attribute setters will affect the root view from the XML layout.\n\nIf you want to modify attributes of the child views from the XML - you should\nuse `withId()` to assign a renderable to a view with the given ID. You may not\nfollow the hierarchy of the child views, e.g. all your `withId()` calls may be\nnested as the views are nested in the XML, or may be direct children of the\n`xml()` renderable. `xml()` calls can be nested, too.\n\n`xml()` also allows you to create views that can not be inflated from Java code\nbut only from XML (such as hostozintal progress bars) or any other views where\nAttributeSet must be given or the views that rely on the `onFinishInflate`\nmethod to be called.\n\n`withId()` call is not limited to XML views, it can be used for views declared\nin code that have IDs. So if you have a custom viewgroup that creates its child\nviews automatically and assigns some IDs to them - you can still modify their\nproperties using `withId()`. Also, if any views are created inside the\n`withId()` or `xml()` - they will be appeneded to the view group:\n\n``` Kotlin\nv(MyComponent::class) {\n    withId(R.id.child_view) {\n        // R.id.child_view was implicitely created in MyComponent's constructor\n        // but we still can modify it here\n    }\n\n    textView {\n        // This textView will be appeneded to MyComponent layout\n    }\n}\n```\n\n## License\n\nCode is distributed under MIT license, feel free to use it in your proprietary\nprojects as well.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finkremental%2Finkremental","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finkremental%2Finkremental","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finkremental%2Finkremental/lists"}