flutter 如何减少 widget rebuild 的次数?

发布于 1周前 作者 sinazl 来自 Flutter

Flutter 如何减少 Widget 重建次数?

减少 Widget 重建次数是提升 Flutter 应用性能的重要手段。以下是一些优化方法,结合代码和完整案例来展示如何实现这些优化。

1. 使用 const 构造

在不可变的 Widget(如文本、静态图片等)前加上 const 关键字,确保它们只构建一次。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    const title = Text('This is a constant text');
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Flutter Demo')),
        body: Center(child: title),
      ),
    );
  }
}

2. 分离状态和布局

将有状态逻辑和布局分离,尽量让 State 管理最小区域。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class CounterWidget extends StatefulWidget {
  @override
  _CounterWidgetState createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int count = 0;

  void increment() {
    setState(() {
      count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Text('Count: $count'),
        ButtonBar(
          alignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              child: Text('Increment'),
              onPressed: increment,
            ),
          ],
        ),
      ],
    );
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Flutter Demo')),
        body: Center(child: CounterWidget()),
      ),
    );
  }
}

3. 使用 AutomaticKeepAliveClientMixin

ListViewPageView 中,保持滑出视图的 Widget 不被重建。

import 'package:flutter/material.dart';

class KeepAlivePage extends StatefulWidget {
  @override
  _KeepAlivePageState createState() => _KeepAlivePageState();
}

class _KeepAlivePageState extends State<KeepAlivePage> with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(
      appBar: AppBar(title: Text('Keep Alive Page')),
      body: Center(child: Text('This page will not be rebuilt')),
    );
  }
}

void main() {
  runApp(MaterialApp(
    home: PageView(
      children: [
        KeepAlivePage(),
        Center(child: Text('Page 2')),
      ],
    ),
  ));
}

4. 使用 ValueListenableBuilderStreamBuilder

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final ValueNotifier<int> counter = ValueNotifier(0);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('StreamBuilder Demo')),
        body: Column(
          children: [
            ValueListenableBuilder<int>(
              valueListenable: counter,
              builder: (_, value, __) {
                return Text('Counter: $value');
              },
            ),
            ElevatedButton(
              onPressed: () {
                counter.value++;
              },
              child: Text('Increment'),
            ),
          ],
        ),
      ),
    );
  }
}

5. 避免使用 setState 更新大范围区域

// 参照前面的 CounterWidget 示例,局部更新 count 状态

6. 使用 BuilderLayoutBuilder

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Builder Demo')),
        body: Builder(
          builder: (context) {
            return Center(
              child: ElevatedButton(
                onPressed: () {
                  // 局部更新,不会触发 Scaffold 的重建
                  ScaffoldMessenger.of(context).showSnackbar(
                    SnackBar(content: Text('Button Pressed')),
                  );
                },
                child: Text('Show Snackbar'),
              ),
            );
          },
        ),
      ),
    );
  }
}

7. 使用 RepaintBoundary 控制重绘

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('RepaintBoundary Demo')),
        body: Center(
          child: RepaintBoundary(
            child: AnimatedContainer(
              duration: Duration(seconds: 1),
              color: Colors.blue,
              width: 200,
              height: 200,
              child: Center(child: Text('This will not repaint the entire tree')),
            ),
          ),
        ),
      ),
    );
  }
}

8. 优化 ListViewGridView

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('ListView Demo')),
        body: ListView.builder(
          itemCount: 100,
          itemBuilder: (_, index) {
            return ListTile(
              title: Text('Item $index'),
            );
          },
        ),
      ),
    );
  }
}

9. 使用 memoization 技术

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final Map<int, String> memoizationCache = {};

  String expensiveComputation(int input) {
    // 模拟一个昂贵的计算
    return memoizationCache.putIfAbsent(input, () => input.toString() * 2);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Memoization Demo')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: List.generate(10, (index) {
              final result = expensiveComputation(index);
              return Text('Result $index: $result');
            }),
          ),
        ),
      ),
    );
  }
}

10. Avoid Unnecessary Parent Widgets

// 避免嵌套过多的无用父 Widget,保持 widget 树的简洁
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Simplified Widget Tree')),
        body: Center(
          child: Text('Avoid unnecessary parents'),
        ),
      ),
    );
  }
}

通过以上方法,你可以有效减少 Flutter 应用中的 Widget 重建次数,从而提升应用的性能和响应速度。


更多关于flutter 如何减少 widget rebuild 的次数?的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于flutter 如何减少 widget rebuild 的次数?的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在 Flutter 中,减少 Widget 的重建次数对于提升应用的性能和用户体验至关重要。Widget 的重建通常会导致不必要的计算和渲染,从而影响性能。以下是一些常见的方法,通过代码示例来展示如何减少 Widget 的重建次数。

1. 使用 constfinal

使用 constfinal 关键字可以确保变量在编译时就已经确定,从而避免在运行时进行不必要的计算。

class MyWidget extends StatelessWidget {
  final String title;

  const MyWidget(this.title);

  @override
  Widget build(BuildContext context) {
    return Text(title);
  }
}

2. 使用 memoization

通过使用 memoization 技术,如 RememberValueKey,可以避免重复构建相同的 Widget。

import 'package:flutter/material.dart';

class MyWidget extends StatelessWidget {
  final String data;

  MyWidget(this.data);

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<String>(
      valueListenable: ValueNotifier(data),
      builder: (_, value, __) {
        return Text(value);
      },
    );
  }
}

3. 使用 Provider 状态管理

使用 Provider(如 riverpodprovider)可以在多个 Widget 之间共享状态,从而避免不必要的重建。

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

final myDataProvider = StateProvider<String>((ref) {
  ref.value = 'Initial Data';
});

class MyApp extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final data = ref.watch(myDataProvider);

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Provider Example')),
        body: Center(child: Text(data)),
      ),
    );
  }
}

4. 使用 InheritedWidget

虽然 InheritedWidget 较为底层,但在某些情况下,它可以有效地避免不必要的重建。

import 'package:flutter/material.dart';

class MyData extends InheritedWidget {
  final String data;

  MyData({
    required Widget child,
    required this.data,
  }) : super(child: child);

  static MyData? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyData>();
  }

  @override
  bool updateShouldNotify(MyData oldDelegate) {
    return data != oldDelegate.data;
  }
}

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final data = MyData.of(context)?.data ?? 'Default Data';
    return Text(data);
  }
}

void main() {
  runApp(
    MyData(
      data: 'Hello, World!',
      child: MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: Text('InheritedWidget Example')),
          body: Center(child: MyWidget()),
        ),
      ),
    ),
  );
}

5. 使用 RepaintBoundaryOpacity

RepaintBoundary 可以防止子 Widget 的重建影响父 Widget,而 Opacity 可以用来包裹不需要频繁更新的 Widget。

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      child: Opacity(
        opacity: 1.0, // This will not cause rebuilds unless opacity changes
        child: Text('This text will not cause its parent to rebuild'),
      ),
    );
  }
}

通过以上方法,你可以有效地减少 Flutter 应用中 Widget 的重建次数,从而提升应用的性能和用户体验。每种方法都有其适用的场景,根据具体情况选择合适的技术是关键。

回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!