<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Flutter性能揭秘之RepaintBoundary

          共 10648字,需瀏覽 22分鐘

           ·

          2023-08-18 22:25

          點擊上方藍字關注我,知識會給你力量


          Flutter會在屏幕上繪制Widget。如果一個Widget的內容需要更新,那就只能重繪了。盡管如此,F(xiàn)lutter同樣會重新繪制一些Widget,而這些Widget的內容仍有部分未被改變。這可能會影響應用程序的執(zhí)行性能,有時影響會非常巨大。如果您正在尋找一種方法,來防止不必要的部分重繪,您可以考慮利用RepaintBoundary。

          在這篇博客理,我們將探討Flutter中的RepaintBoundary。我們將看到如何實現(xiàn)RepaintBoundary的演示程序以及如何在您的flutter應用程序中使用它。

          RepaintBoundary

          RepaintBoundary類是Null安全的。首先,你需要了解什么是Flutter中的RepaintBoundary。它是一個為它的Child設置不同的展示層級的Widget。這個Widget為它的Child設置了一個不同的展示層級,如果一個子樹與它周圍的部分相比,會在意想不到的短時間內重新繪制,F(xiàn)lutter建議你使用RepaintBoundary來進一步提高性能。

          為什么需要使用RepaintBoundary呢。

          Flutter Widget與RenderObjects有關。一個RenderObject有一個叫做paint的函數(shù),它被用來執(zhí)行繪畫過程。盡管如此,無論相關組件的內容是否發(fā)生變化,都可以使用繪制方法。這是因為,如果其中一個RenderObjects被設定為dirty,F(xiàn)lutter可能會對類似Layer中的其他RenderObjects進行重新繪制。當一個RenderObject需要利用RenderObject.markNeedsPaint進行重繪的時候,它就會建議它最接近的前輩進行重繪。祖先也會對它的前輩做同樣的事情,直到根RenderObject。當一個RenderObject的paint策略被啟動時,它在類似層中的所有相關RenderObjects都將被重新paint。

          而有時,當一個RenderObject應該被重繪時,類似層中的其他RenderObjects不應該被重繪,因為它們的繪制產(chǎn)物保持不變。因此,如果我們只是對某些RenderObjects進行重繪,那會更好。利用RepaintBoundary可以幫助我們在渲染樹上限制markNeedsPaint的生成,在渲染樹下限制paintChild的生成。

          RepaintBoundary可以將先前的渲染對象與相關的渲染對象解耦。通過這種方式,只對內容發(fā)生變化的子樹進行重繪是可行的。利用RepaintBoundary可以進一步提高應用程序的執(zhí)行效率,特別是當不應該被重繪的子樹需要大量的工作來重繪時。

          我們將做一個簡單的演示程序,背景是利用CustomPainter繪制的,有10000個橢圓。同時還有一個光標,在客戶接觸到屏幕的最后一個位置后移動。下面是沒有RepaintBoundary的代碼。

          示例

          在正文中,我們將創(chuàng)建一個Stack widget。在里面,我們將添加一個StackFit.expand,并添加兩個部件:_buildBackground(),和_buildCursor()。我們將定義以下代碼。

          Stack(
            fit: StackFit.expand,
            children: <Widget>[
              _buildBackground(),
              _buildCursor(),
            ],
          ),


          _buildBackground() widget

          在_buildBackground()小組件中。我們將返回CustomPaint() widget。在里面,我們將在繪畫器上添加BackgroundColor類。我們將在下面定義。另外,我們將添加isComplex參數(shù)為true,這意味著是否提示這個圖層的繪畫應該被緩存,willChange是false意味著是否應該告訴光柵緩存,這個繪畫在下一幀可能會改變。

          Widget _buildBackground() {
            return CustomPaint(
              painter:  BackgroundColor(MediaQuery.of(context).size),
              isComplex: true,
              willChange: false,
            );
          }


          BackgroundColor class

          我們將創(chuàng)建一個BackgroundColor來擴展CustomPainter。

          import 'dart:math';
          import 'package:flutter/material.dart';

          class BackgroundColor extends CustomPainter {

            static const List<Color> colors = [
              Colors.orange,
              Colors.purple,
              Colors.blue,
              Colors.green,
              Colors.purple,
              Colors.red,
            ];

            Size _size;
            BackgroundColor(this._size);

            @override
            void paint(Canvas canvas, Size size) {
              final Random rand = Random(12345);

              for (int i = 0; i < 10000; i++) {
                canvas.drawOval(
                    Rect.fromCenter(
                      center: Offset(
                        rand.nextDouble() * _size.width - 100,
                        rand.nextDouble() * _size.height,
                      ),
                      width: rand.nextDouble() * rand.nextInt(150) + 200,
                      height: rand.nextDouble() * rand.nextInt(150) + 200,
                    ),
                    Paint()
                      ..color = colors[rand.nextInt(colors.length)].withOpacity(0.3)
                );
              }
            }

            @override
            bool shouldRepaint(BackgroundColor other) => false;
          }


          _buildCursor() widget

          在這個Widget,我們將返回Listener Widget。我們將在onPointerDown/Move方法中添加_updateOffset()組件,并添加CustomPaint。在里面,我們將添加一個Key和CursorPointer類。我們將在下面定義。另外,我們將添加ConstrainedBox()。

          Widget _buildCursor() {
            return Listener(
              onPointerDown: _updateOffset,
              onPointerMove: _updateOffset,
              child: CustomPaint(
                key: _paintKey,
                painter: CursorPointer(_offset),
                child: ConstrainedBox(
                  constraints: BoxConstraints.expand(),
                ),
              ),
            );
          }


          CursorPointer class

          我們將創(chuàng)建一個CursorPointer來擴展CustomPainter。

          import 'package:flutter/material.dart';

          class CursorPointer extends CustomPainter {

            final Offset _offset;

            CursorPointer(this._offset);

            @override
            void paint(Canvas canvas, Size size) {
              canvas.drawCircle(
                _offset,
                10.0,
                new Paint()..color = Colors.green,
              );
            }

            @override
            bool shouldRepaint(CursorPointer old) => old._offset != _offset;
          }

          當我們運行應用程序時,我們應該得到下面屏幕的輸出,如屏幕下的視頻。如果你試圖在屏幕上移動指針,應用程序將非常滯后,因為它重新繪制背景,需要昂貴的計算。

          下面,我們將添加RepaintBoundary。解決上述問題的答案是將CustomPaint部件包裝成RepaintBoundary的子Widget。

          Widget _buildBackground() {
            return RepaintBoundary(
              child: CustomPaint(
                painter:  BackgroundColor(MediaQuery.of(context).size),
                isComplex: true,
                willChange: false,
              ),
            );
          }

          當我們運行應用程序時,我們應該得到屏幕的輸出,就像屏幕下面的視頻一樣。有了這個簡單的改變,現(xiàn)在當Flutter重繪光標時,背景就不需要重繪了。應用程序應該不再是滯后的了。

          整個代碼如下所示。

          import 'package:flutter/material.dart';
          import 'package:flutter_repaint_boundary_demo/background_color.dart';
          import 'package:flutter_repaint_boundary_demo/cursor_pointer.dart';

          class HomePage extends StatefulWidget {

            @override
            State createState() => new _HomePageState();
          }

          class _HomePageState extends State<HomePage{

            final GlobalKey _paintKey = new GlobalKey();
            Offset _offset = Offset.zero;

            Widget _buildBackground() {
              return RepaintBoundary(
                child: CustomPaint(
                  painter:  BackgroundColor(MediaQuery.of(context).size),
                  isComplex: true,
                  willChange: false,
                ),
              );
            }

            Widget _buildCursor() {
              return Listener(
                onPointerDown: _updateOffset,
                onPointerMove: _updateOffset,
                child: CustomPaint(
                  key: _paintKey,
                  painter: CursorPointer(_offset),
                  child: ConstrainedBox(
                    constraints: BoxConstraints.expand(),
                  ),
                ),
              );
            }

            @override
            Widget build(BuildContext context) {
              return Scaffold(
                appBar: AppBar(
                  automaticallyImplyLeading: false,
                  backgroundColor: Colors.cyan,
                  title: const Text('Flutter RepaintBoundary Demo'),
                ),
                body: Stack(
                  fit: StackFit.expand,
                  children: <Widget>[
                    _buildBackground(),
                    _buildCursor(),
                  ],
                ),
              );
            }

            _updateOffset(PointerEvent event) {
              RenderBox? referenceBox = _paintKey.currentContext?.findRenderObject() as RenderBox;
              Offset offset = referenceBox.globalToLocal(event.position);
              setState(() {
                _offset = offset;
              });
            }
          }


          總結

          在文章中,我解釋了Flutter中RepaintBoundary的基本結構;你可以根據(jù)你的選擇來修改這個代碼。這是我對RepaintBoundary On User Interaction的一個小的介紹,它在使用Flutter時是可行的。

          翻譯自 https://medium.flutterdevs.com/repaintboundary-in-flutter-9e2f426ff579

          向大家推薦下我的網(wǎng)站 https://www.yuque.com/xuyisheng  點擊原文一鍵直達

          專注 Android-Kotlin-Flutter 歡迎大家訪問



          往期推薦


          本文原創(chuàng)公眾號:群英傳,授權轉載請聯(lián)系微信(Tomcat_xu),授權后,請在原創(chuàng)發(fā)表24小時后轉載。
          < END >
          作者:徐宜生

          更文不易,點個“三連”支持一下??


          瀏覽 284
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  在线观看日本国产 | 91探花视频在线观看 | 色色综合网络 | 伊人激情综合 | 操鼻视频素材直接看 |