性能是任何应用程序或产品的关键因素,多种因素影响着它。一般来说,当您在Flutter中构建应用程序时,性能结果足够好,但您的应用程序的性能仍可能面临问题。
这就是为什么你需要在开发本身中注意Flutter应用程序的最佳实践和性能改进--提前修复问题,为你的最终用户提供完美的体验。
本文的目的是引导您了解Flutter应用程序的性能改进的具体最佳实践。我将告诉您如何。
1.避免重新构建小部件
最常见的性能反模式之一是使用setState 来重建 [StatefulWidgets](https://blog.logrocket.com/difference-between-stateless-stateful-widgets-flutter/).每次用户与小组件互动时,整个视图都会被刷新,影响到脚手架、后台小组件和容器--这大大增加了应用程序的加载时间。
在这种情况下,只重建我们必须要更新的东西是一个很好的策略。这可以通过Flutter中的Bloc模式来实现。像flutter_bloc、MobX和Provider这样的包是流行的。
但是,你知道这也可以在没有任何外部包的情况下实现吗?让我们看一下下面的例子。
class _CarsListingPageState extends State<CarsListingPage> {
final _carColorNotifier = ValueNotifier<CarColor>(Colors.red);
Random _random = new Random();
void _onPressed() {
int randomNumber = _random.nextInt(10);
_carColorNotifier.value =
Colors.primaries[randomNumber % Colors.primaries.lengths];
}
@override
void dispose() {
_carColorNotifier.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
print('building `CarsListingPage`');
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: _onPressed,
child: Icon(Icons.colorize),
),
body: Stack(
children: [
Positioned.fill(
child: BackgroundWidget(),
),
Center(
child: ValueListenableBuilder(
valueListenable: _colorNotifier,
builder: (_, value, __) => Container(
height: 100,
width: 100,
color: value,
),
),
),
],
),
);
}
}
类_CarsListingPageState ,描述了基于状态的可能行动的行为,如_onPressed 。框架的build 方法正在根据提供给该方法的context 建立一个Widget 的实例。它创建了一个floatingActionButton 的实例,并指定了诸如颜色、高度和宽度等属性。
当用户在屏幕上按下FloatingActionButton ,onPressed 被调用,并从 _CarsListingPageState 调用_onPressed 。然后从主调色板中分配一个随机的颜色,然后通过builder 返回,颜色被填充在屏幕的中心。
在这里,每次,上述代码中的build 方法都不会在控制台打印输出building CarsListingPage 。这意味着这个逻辑工作正常--它只是在构建我们需要的小部件。
2.利用常量小部件
普通小部件和常量小部件之间有什么区别?就像定义所建议的那样,将const ,在编译时将初始化该部件。
这意味着将widget声明为常量将在编译时而不是运行时初始化widget和它的所有附属物。这也将允许你尽可能多地利用widget,同时避免不必要的重建。
下面是一个如何利用常量widget的例子。
class _CarListingPageState extends State<CarListingPage> {
int _counter = 0;
void _onPressed() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: _onPressed,
child: Icon(Icons.play_arrow),
),
body: Stack(
children: [
Positioned.fill(
child: const DemoWidget(),
),
Center(
child: Text(
_counter.toString(),
)),
],
),
);
}
}
class DemoWidget extends StatelessWidget {
const DemoWidget();
@override
Widget build(BuildContext context) {
print('building `DemoWidget`');
return Image.asset(
'assets/images/logo.jpg',
width: 250,
);
}
}
_CarListingPageState 类指定了一个状态:_onPressed ,它调用setState 并增加_counter 的值。build 方法生成了一个FloatingActionButton 和树中的其他元素。DemoWidget 里面的第一行创建了一个新的实例,并声明它是一个常量。
每次按下FloatingActionButton ,计数器就会增加,计数器的值会写在屏幕上的子项里面。在这个执行过程中,DemoWidget 被重复使用,整个小组件的再生被跳过,因为它被声明为一个常量小组件。
在下面的GIF中可以看到,"构建DemoWidget"的语句只在小部件第一次构建时被打印一次,然后就被重复使用。
然而,每次你热重载或重启应用程序时,你会看到打印的语句 "构建DemoWidget"。

3.高效加载列表项--按需加载
当处理列表项时,开发者通常使用小工具SingleChildScrollView 和Column 的组合。
当处理大型列表时,如果你继续使用这组相同的部件,事情很快就会变得很混乱。这是因为每个项目都被附加到列表上,然后在屏幕上渲染,这增加了系统的整体负载。
在这种情况下,使用ListView builder是个好主意。这可以在很高的水平上提高性能。让我们来看看一个构建器对象的例子。
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('Row: ${items[index]}'),
);},);
4.使用async/await
在编写执行流程时,确定代码是允许同步运行还是异步运行是很重要的。异步代码更难调试和改进,但仍有一些方法可以在Flutter中编写异步代码,这包括使用Future 、async/await ,以及其他。
当与async 结合时,代码的可读性会得到改善,因为编写代码的结构和模式被遵循。另一方面,由于它能够在需要的地方招待故障安全策略--在这种情况下,try ... catch ,整体执行性能得到了提高。让我们来看看下面的例子。
// Inappropriate
Future<int> countCarsInParkingLot() {
return getTotalCars().then((cars) {
return cars?.length ?? 0;
}).catchError((e) {
log.error(e);
return 0;
});
}
// Appropriate
Future<int> countCarsInParkingLot() async { // use of async
try {
var cars = await getTotalCars();
return cars?.length ?? 0;
} catch (e) {
log.error(e);
return 0;
}
}
5.有效地使用运算符
Flutter有很多语言特有的功能。其中之一就是运算符。
如果你想减少开发时间,写出健壮的代码以避免逻辑错误,同时提高代码的可读性,建议使用空值检查运算符、可空值运算符和其他适当的运算符。
让我们看一下下面的一些例子。
car = van == null ? bus : audi; // Old pattern
car = audi ?? bus; // New pattern
car = van == null ? null : audi.bus; // Old pattern
car = audi?.bus; // New pattern
(item as Car).name = 'Mustang'; // Old pattern
if (item is Car) item.name = 'Mustang'; // New pattern
6.利用插值技术
使用操作符+ ,进行字符串操作和连锁是一种常见的做法。我们不这样做,而是利用字符串插值,这样可以提高代码的可读性,减少出错的几率。
// Inappropriate
var discountText = 'Hello, ' + name + '! You have won a brand new ' + brand.name + 'voucher! Please enter your email to redeem. The offer expires within ' + timeRemaining.toString() ' minutes.';
// Appropriate
var discountText = 'Hello, $name! You have won a brand new ${brand.name} voucher! Please enter your email to redeem. The offer expires within ${timeRemaining} minutes.';
正如规定的那样,内联访问变量提高了指定文本的可读性与价值,而且由于字符串被分割成更少的部分,代码变得不容易出错。
7.减少你的应用程序大小
在你的开发过程中,真的很容易在你的代码中添加大量的包。正如你可能知道的,这可能会变成臃肿的软件。
让我们用一个Android应用程序作为例子。你可以使用Gradle,一个强大的开源构建工具,带有大量的配置选项,以减少应用程序的大小。
你还可以生成安卓应用包,这是谷歌推出的一个新的打包系统。
应用包在多个方面都很高效。只有特定目标设备所需的代码才会从Google Play商店下载。这是因为谷歌应用商店只为目标设备的屏幕密度、平台架构、支持的硬件功能等重新打包和运送必要的文件和资源。
Google Play控制台统计显示,在大多数情况下,当你选择应用包而不是APK时,应用的下载大小会减少40%至60%。
生成一个应用程序捆绑包的命令是。
flutter build appbundle
为了混淆Dart语言代码,你需要在构建命令中使用obfuscate 和--split-debug-info 标志。该命令看起来像这样。
flutter build apk --obfuscate --split-debug-info=/<project-name>/<directory>
上述命令生成了一个符号映射文件。这个文件对去混淆堆栈痕迹很有用。
ProGuard和保持规则
下面是一个应用了ProGuard和其他配置的应用程序级build.gradle 文件的例子。
android {
...
def proguard_list = [
"../buildsettings/proguard/proguard-flutter.pro",
"../buildsettings/proguard/proguard-firebase.pro",
"../buildsettings/proguard/proguard-google-play-services.pro",
...
]
buildTypes {
release {
debuggable false // make app non-debuggable
crunchPngs true // shrink images
minifyEnabled true // obfuscate code and remove unused code
shrinkResources true // shrink and remove unused resources
useProguard true // apply proguard
proguard_list.each {
pro_guard -> proguardFile pro_guard
}
signingConfig signingConfigs.release
}
}
减少APK大小的最佳做法之一是将ProGuard规则应用于你的Android应用。ProGuard应用的规则可以从最终生成的包中删除未使用的代码。在构建生成过程中,上述代码使用ProGuard从指定位置对代码和资源应用各种配置。
下面是一个为Firebase指定的ProGuard规则的例子。
-keepattributes EnclosingMethod
-keepattributes InnerClasses
-dontwarn org.xmlpull.v1.**
-dontnote org.xmlpull.v1.**
-keep class org.xmlpull.** { *; }
-keepclassmembers class org.xmlpull.** { *; }
上面的声明被称为保持规则。保持规则是在ProGuard配置文件内指定的。这些规则定义了在代码缩减和混淆阶段,当keep规则的指定模式匹配时,该如何处理文件、属性、类、成员声明和其他注释。
你可以使用破折号和声明规则关键字来指定哪些要保留,哪些要忽略,像这样。
-keep class org.xmlpull.** { *; }
上述规则在应用ProGuard时,在代码缩减阶段不会删除该类或任何类的内容。
在使用时,你仍然需要谨慎,因为如果操作不当,它可能会引入错误。原因是,如果你指定的规则删除了一个代码块、一个类或任何被声明并用于运行代码执行的成员,该规则可能会引入编译时错误、运行时错误,甚至是致命的错误,如空指针异常。
你可以从安卓官方开发者社区了解更多关于如何以正确方式实现ProGuard规则的信息。
.IPA iOS的构建步骤
同样,对于iOS,你需要执行以下的.IPA 构建步骤。
- 进入XCode,在右窗格中的 "档案"部分点击 "分发应用程序 "。
- 在选择分发方法后,例如开发,然后点击 "下一步"按钮,进入App Thinning部分。

- 在App Thinning部分,选择All compatible device variants。

- 然后选择Rebuild from BitcodeandStrip Swift symbols。然后签署并导出
.IPA文件。它还会生成一个应用瘦身尺寸报告文件。
总结
在这篇文章中,我们已经讨论了提高Flutter应用程序性能的技术。虽然Flutter作为一个框架有很多功能,并且不断有新的更新,但性能始终是一个关键的考虑因素。
在占领全球市场时,应用程序的性能一直是并将是一个巨大的决定性因素。当考虑到移动应用程序的不同方面,如应用程序的大小、设备分辨率、代码的执行速度和硬件能力,提高性能可以带来巨大的差异,特别是在针对大量受众的时候。
The postPerformance Improvement for mobile apps in Flutterappeared first onLogRocket Blog.