[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-9499":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":14,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":18,"rankGlobal":10,"rankLanguage":10,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":22,"hasPages":20,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":26,"readmeContent":27,"aiSummary":28,"trendingCount":16,"starSnapshotCount":16,"syncStatus":29,"lastSyncTime":30,"discoverSource":31},9499,"keframe","LianjiaTech\u002Fkeframe","LianjiaTech","Components that optimize Flutter fluency.（Flutter 流畅度优化的通用方案，轻松解决卡顿问题）","",null,"Dart",1005,107,31,9,0,1,57.7,"MIT License",false,"master",true,[24,25],"flutter","optimization","2026-06-12 04:00:45","# Flutter fluency optimization component \"Keframe\"\n\n![image](https:\u002F\u002Fuser-images.githubusercontent.com\u002F40540394\u002F123203124-6eb83800-d4e8-11eb-9fd8-8b53e79c258f.png)\n\n   + [Page switching fluency improved:](#page-switching-fluency-improved)\n   + [How to use](#how-to-use)\n     - [Project depend on：](#project-depend-on)\n     - [Quick learning](#quick-learning)\n   + [Constructor Description:](#constructor-description)\n   + [Example explanation:](#example-explanation-)\n     - [1. The actual item size in the list is known](#1-the-actual-item-size-in-the-list-is-known)\n     - [2. The actual item height in the list is unknown](#2-the-actual-item-height-in-the-list-is-unknown)\n     - [3. Estimate the number of items in a screen](#3-estimate-the-number-of-items-in-a-screen)\n     - [4. Non-list scenarios](#4-non-list-scenarios)\n   + [The cost of framing](#the-cost-of-framing)\n   + [Before and after optimization demo](#before-and-after-optimization-demo)\n   \nLanguage: English | [中文简体](README-ZH.md)\n\n[![null-safe](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fnullsafe-2.0.6-brightgreen)](https:\u002F\u002Fpub.dev\u002Fpackages\u002Fkeframe)\n[![null-safe](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fnormal-1.0.3-brightgreen)](https:\u002F\u002Fpub.dev\u002Fpackages\u002Fkeframe)\n[![GitHub stars](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fstars\u002FLianjiaTech\u002Fkeframe)](https:\u002F\u002Fgithub.com\u002FLianjiaTech\u002Fkeframe\u002Fstargazers)\n[![GitHub license](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flicense\u002FLianjiaTech\u002Fkeframe)](https:\u002F\u002Fgithub.com\u002FLianjiaTech\u002Fkeframe\u002Fblob\u002Fmaster\u002FLICENSE)\n\nOptimize for lag caused by builds, such as page switches or rapid scrolling of complex lists, through frame-splitting rendering.\n\nThe following is [Example](app-profile.apk)(you can download it directly) running in VIVO X23 (Snapdragon 660).  Comparison of collected data indicators before and after optimization of 200 frames under the same operation (The demo is at the end of the article)\n\n| Before optimization | after optimization |\n| --- | --- |\n| \u003Cimg src=\"https:\u002F\u002Fp3-juejin.byteimg.com\u002Ftos-cn-i-k3u1fbpfcp\u002F4233166557ec4b4da0133fe8a9b17325~tplv-k3u1fbpfcp-watermark.image\" alt=\"优化前\" style=\"zoom:50%;\" \u002F> | \u003Cimg src=\"https:\u002F\u002Fp9-juejin.byteimg.com\u002Ftos-cn-i-k3u1fbpfcp\u002F6db4a156eaa14bb8b6acb27549788b63~tplv-k3u1fbpfcp-watermark.image\" alt=\"优化后\" style=\"zoom:50%;\" \u002F> |\n\nMonitoring tools from:  [fps_monitor](https:\u002F\u002Fgithub.com\u002FNayuta403\u002Ffps_monitor)\n\n- Fluency: FPS greater than 55, which means less than 18ms per frame\n- Good: FPS between 30-55, i.e. 18ms-33ms per frame\n- Slight lag: FPS between 15-30, i.e. 33ms-67ms per frame\n- Caton: FPS less than 15, which means a frame time greater than 66.7ms\n\nAfter using frame splitting optimization, the number of lag decreased from an average of 33.3 frames to only one in 200 frames, and the slight lag decreased from 188ms to 90ms. The phenomenon of lag is greatly reduced, the proportion of fluid frames is significantly increased, and the overall performance is smoother. Below are the details.\n\n|                                                  | Before optimization | after optimization |\n| ------------------------------------------------ | ------------------- | ------------------ |\n| The average number of frames appears a lag     | 33.3                | 200                |\n| The average number of frames with a slight lag | 8.6                 | 66.7               |\n| Most time consuming                              | 188.0ms             | 90.0ms             |\n| The average time                                 | 27.0ms              | 19.4ms             |\n| Fluency frame ratio                              | 40%                 | 64.5%              |\n\n****\n\n###  Page switching fluency improved:\n\nWhen opening a page or Tab switch, the system will render the entire page and complete the page switch with animation. For complex pages, there will also be a frame lag. \n\nWith the framing component, the page build is disassembled frame by frame and viewed through the performance tool in DevTools. During switching, the peak value of a frame is reduced from **112.5ms to 30.2ms**, and the overall switching process is more smooth.\n\n| ![image.png](https:\u002F\u002Fp1-juejin.byteimg.com\u002Ftos-cn-i-k3u1fbpfcp\u002Fc0ce341f0a2d4fceb0ad123fd4834ce2~tplv-k3u1fbpfcp-watermark.image) | ![image.png](https:\u002F\u002Fp9-juejin.byteimg.com\u002Ftos-cn-i-k3u1fbpfcp\u002F0c571a755ac84f39b52d57a13856a243~tplv-k3u1fbpfcp-watermark.image) |\n| ------------------------------------------------------------ | ------------------------------------------------------------ |\n\n***\n\n### How to use\n\n#### Project depend on：\n\nAdd a dependency on `keframe` to `pubspec.yaml`\n\n```yaml\ndependencies:\n  keframe: version\n```\n\nComponents distinguish only the normal and  null-safe versions\n\nThe normal version uses : `1.0.3`\n\nThe null-safe version uses ：`2.0.6`\n\n\n#### Quick learning\n\nAs shown in the figure below ：\n\n![image.png](https:\u002F\u002Fp9-juejin.byteimg.com\u002Ftos-cn-i-k3u1fbpfcp\u002F83d16f3b2a3e45b79fc73d7a52774696~tplv-k3u1fbpfcp-watermark.image)\n\nSuppose the page now consists of four parts A, B, C and D, each of which takes 10ms and is built as 40ms at page time. \n\nUse the frameseparateWidget component to nest each section. A simple placeholder is rendered on the first frame of the page, and A, B, C, and D are rendered on the next four frames.\n\nFor the list, nested `FrameSeparateWidget` in each item and nested `ListView` in `SizeCacheWidget`.\n\n![image.png](https:\u002F\u002Fp6-juejin.byteimg.com\u002Ftos-cn-i-k3u1fbpfcp\u002Fffecd49bf9ba4379984a22ef79663104~tplv-k3u1fbpfcp-watermark.image)\n\n***\n\n### Constructor Description:\n\nFrameSeparateWidget ：A frame-splitting component that renders nested widgets in a single frame.\n\n| type   | name        | required | describe                                                     |\n| ------ | ----------- | -------- | ------------------------------------------------------------ |\n| Key    | key         | no       |                                                              |\n| int    | index       | no       | frame component id, using SizeCacheWidget scenario will pass, and to maintain the index in the SizeCacheWidget corresponding Size information |\n| Widget | child       | yes      | the actual need to render the widget                         |\n| Widget | placeHolder | no       | placeholder widget, try to set up a simple placeholder, not the default is the Container () |\n\nSizeCacheWidget：Cache size information for **actual widgets nested by framing components in child nodes **.\n\n| type   | name          | required | describe                                                     |\n| ------ | ------------- | -------- | ------------------------------------------------------------ |\n| Key    | key           | no       |                                                              |\n| Widget | child         | yes      | if include framing component in the child nodes, the **cache is the actual widget size** |\n| int    | estimateCount | no       | estimates the number of child nodes on the screen, can enhance the response speed of the fast scroll |\n\n***\n\n### Example explanation:\n\nCaton's pages are often rendered by multiple complex widgets at the same time. By nested `FrameSeparateWidget` for complex widgets. When rendering, the Framing Component renders multiple `palceHolder` simultaneously in the first frame, and then renders complex sub-items in successive frames to improve page fluency.\n\nFor example:\n\n```dart\nListView.builder(\n              itemCount: childCount,\n              itemBuilder: (c, i) => CellWidget(\n                color: i % 2 == 0 ? Colors.red : Colors.blue,\n                index: i,\n              ),\n            )\n```\n\nThe height of `cellWidget` is 60, and three components of `TextField` are nested inside (the overall construction time is about 9ms).\n\nOptimization simply involves nested framing components for each item and setting `placeHolder` for each item (placeHolder should be as simple as possible and should look like the actual item).\n\nIn the `ListView` case, nested `SizeCacheWidget` is recommended, and the pre-load `cacheExtent`  is recommended to be larger, such as 500 (the default is 250), to improve the slow sliding experience.\n\nFor example:\n\n```dart\nSizeCacheWidget(\n              child: ListView.builder(\n                cacheExtent: 500,\n                itemCount: childCount,\n                itemBuilder: (c, i) => FrameSeparateWidget(\n                  index: i,\n                  placeHolder: Container(\n                    color: i % 2 == 0 ? Colors.red : Colors.blue,\n                    height: 60,\n                  ),\n                  child: CellWidget(\n                    color: i % 2 == 0 ? Colors.red : Colors.blue,\n                    index: i,\n                  ),\n                ),\n              ),\n            ),\n```\n\nHere are a few scenarios:\n\n#### 1. The actual item size in the list is known\n\nIf the actual item height is known (each item height is 60), just set the placeholder to match the actual item height. See frame optimization 1 in Example.\n\n```dart\nFrameSeparateWidget(\n                index: i,\n                placeHolder: Container(\n                  color: i % 2 == 0 ? Colors.red : Colors.blue,\n                  height: 60,\u002F\u002F Keep the same height as the actual item\n                ),\n                child: CellWidget(\n                  color: i % 2 == 0 ? Colors.red : Colors.blue,\n                  index: i,\n                ),\n              )\n```\n\n#### 2. The actual item height in the list is unknown\n\nIn the real world, lists are often presented based on data, and there is no way to predict the size of an item at first.\n\nFor example, in example frame optimization 2, `placeHolder` (height 40) is not the same size as the actual item (height 60).\nBecause each item is rendered in a different frame, list `jitter` occurs.\n\n You can set some approximate height for the placeholder. And nested the ListView in the SizeCacheWidget.\n\nFor widgets that have been rendered, the `palceHolder` size is forced and the `cacheExtent` is increased. This way, the rendered item will not jump as it slides back and forth.\n\nFor example, framing optimization 3 in example\n\n```dart\nSizeCacheWidget(\n              child: ListView.builder(\n                cacheExtent: 500,\n                itemCount: childCount,\n                itemBuilder: (c, i) => FrameSeparateWidget(\n                  index: i,\n                  placeHolder: Container(\n                    color: i % 2 == 0 ? Colors.red : Colors.blue,\n                    height: 40,\n                  ),\n                  child: CellWidget(\n                    color: i % 2 == 0 ? Colors.red : Colors.blue,\n                    index: i,\n                  ),\n                ),\n              ),\n            ),\n```\n\nThe actual effect is as follows:\n\n\u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F40540394\u002F123904966-fa780b80-d9a3-11eb-9afe-c1023e265a75.gif\" alt=\"Screenrecording_20210611_194905.gif\" style=\"zoom:50%;\" \u002F>\n\n\n\n#### 3. Estimate the number of items in a screen\n\nIf you can roughly estimate the maximum number of actual items that can be displayed on a screen, say 10. Set the `SizeCacheWidge`  `estimateCount` property to 10*2. Fast scrolling scene builds are more responsive and more memory stable.\n\nFor example, framing optimization 4 in example\n\n```dart\n...\nSizeCacheWidget(\n              estimateCount: 20,\n              child: ListView.builder(\n...\n```\n\nIn addition, you can also nest animations such as transparency\u002Fdisplacement on items to optimize the visual effect.\n\nThe actual effect is as follows:\n\n| ![Screenrecording_20210315_133310.gif](https:\u002F\u002Fp1-juejin.byteimg.com\u002Ftos-cn-i-k3u1fbpfcp\u002Fbb7d1361ae7842df954bb1c559e2ec54~tplv-k3u1fbpfcp-watermark.image) | ![Screenrecording_20210315_133848.gif](https:\u002F\u002Fuser-images.githubusercontent.com\u002F40540394\u002F123905372-c9e4a180-d9a4-11eb-94d0-4190710828f5.gif) |\n| ------------------------------------------------------------ | ------------------------------------------------------------ |\n\n#### 4. Non-list scenarios\n\nFor non-list scenarios, there are generally no fluency issues, but there can still be a lag on first entry.\n\nSimilarly, complex modules can be rendered in different frames to avoid first-time entry delays.\n\nFor example, we will nest the framing component for the bottom action area in the optimization example:\n\n```dart\nFrameSeparateWidget(\n    child: operateBar(),\n    index: -1,\n)\n```\n\n****\n\n### The cost of framing\n\nOf course, the framing scheme is not perfect. In my opinion, there are two main costs:\n\n1. Extra build cost: The build cost of the entire build process changed from \"N * widget cost\" to \"N * (widget + placeholder) cost + The system scheduling N frame cost\". As you can see, the additional overhead is mainly due to the complexity of the placeholder. If the placeholders were simple Containers, the overall build time would probably increase by 10-20% after testing.This extra overhead is negligible for today's mobile devices.\n\n2. Visual change: As demonstrated above, the component will render the item in frames, and the page will visually occupy the space to become the actual widget.\n But in fact, because the list exists in the cache area (it is recommended to increase the cacheExtent), the user does not feel it in the high-end machine or normal sliding situation. \n On lower devices, a quick swipe can feel the transition, but it's better than a heavy stumble.\n \n***\n### If you have any questions, please feel free to contact me. If this inspires you, dont forget start ✨✨✨✨ Thanks\n\n### Before and after optimization demo\n\n| Before optimization | after optimization |\n| --- | --- |\n| ![优化前](https:\u002F\u002Fp1-juejin.byteimg.com\u002Ftos-cn-i-k3u1fbpfcp\u002F2f20f593cc144b72a1df4bdae57a165c~tplv-k3u1fbpfcp-watermark.image) | ![优化后](https:\u002F\u002Fuser-images.githubusercontent.com\u002F40540394\u002F123905087-3c08b680-d9a4-11eb-9485-4cdf21c38ad2.gif) |\n\n## Contributors\n\n\u003C!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->\n\u003C!-- prettier-ignore-start -->\n\u003C!-- markdownlint-disable -->\n\n\u003C!-- markdownlint-restore -->\n\u003C!-- prettier-ignore-end -->\n\n\u003C!-- ALL-CONTRIBUTORS-LIST:END -->\n\n\u003C!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->\n[![All Contributors](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fall_contributors-13-orange.svg?style=flat-square)](#contributors)\n\u003C!-- ALL-CONTRIBUTORS-BADGE:END -->\n","Keframe 是一个优化 Flutter 应用流畅度的组件库，专门解决页面切换、列表快速滚动等场景下的卡顿问题。其核心功能是通过帧分割渲染技术减少构建过程中的延迟，从而显著提升应用性能。Keframe 提供了详细的使用指南和示例代码，帮助开发者轻松集成到现有项目中。适用于需要高度交互性和流畅用户体验的应用开发场景，如复杂的移动应用或游戏界面。该项目采用 Dart 语言编写，遵循 MIT 许可证，并已获得社区广泛认可，拥有近一千个星标。",2,"2026-06-11 03:23:08","top_language"]