项目 Flutter 2 升级 Flutter 3
项目 Flutter 2 升级 Flutter 3
Flutter 3 带来了许多重要的改进和新特性,但升级过程中也遇到了一些挑战。
Flutter 3 的主要新特性
1. 平台支持扩展
- macOS 和 Linux:正式稳定支持
- Web:性能大幅提升,PWA 支持增强
- Windows:生产就绪的桌面应用支持
2. 性能提升
- Material You:全新的 Material Design 支持
- 渲染引擎优化:更流畅的动画和更低的内存占用
- Dart 2.17:更好的性能和开发体验
3. 开发体验改进
- Lint 规则更新:更严格的代码质量检查
- Flutter Inspector:调试工具增强
- Hot Reload:更快的代码重载速度
升级前的准备工作
1. 环境检查
首先检查当前的 Flutter 版本和环境:
1
2
3
4
5
6
7
8
# 查看当前Flutter版本
flutter --version
# 检查Flutter环境
flutter doctor -v
# 查看当前项目的Flutter版本
cat pubspec.yaml | grep flutter
2. 依赖检查
创建依赖检查脚本:
1
2
3
4
5
6
7
8
9
10
#!/bin/bash
# check_dependencies.sh
echo "检查项目依赖兼容性..."
# 检查主要依赖包的Flutter 3兼容性
flutter pub deps --json > deps.json
echo "当前主要依赖包:"
cat pubspec.yaml | grep -A 20 "dependencies:" | grep " " | grep -v "flutter:"
升级步骤详解
步骤 1:Flutter SDK
1
2
3
4
5
6
7
8
9
10
11
12
# 切换到stable channel(如果还没有)
flutter channel stable
# 升级Flutter到最新版本
flutter upgrade
# 验证升级结果
flutter --version
# 应该显示Flutter 3.x.x
# 清理缓存
flutter clean
步骤 2:pubspec.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# pubspec.yaml
name: your_app_name
description: Your app description
publish_to: "none"
version: 1.0.0+1
environment:
# 从Flutter 2.x升级到3.x
sdk: ">=2.17.0 <4.0.0" # 更新Dart SDK版本
flutter: ">=3.0.0" # 指定Flutter最低版本
dependencies:
flutter:
sdk: flutter
# 更新主要依赖包到兼容版本
cupertino_icons: ^1.0.5
http: ^0.13.5
provider: ^6.0.3
shared_preferences: ^2.0.15
# 常用的包
url_launcher: ^6.1.5
image_picker: ^0.8.5+3
dev_dependencies:
flutter_test:
sdk: flutter
# 更新Lint规则
flutter_lints: ^2.0.1 # 从^1.0.0升级
flutter:
uses-material-design: true
assets:
- assets/images/
- assets/icons/
步骤 3:更新 analysis_options.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# analysis_options.yaml
include: package:flutter_lints/flutter.yaml
# Flutter 3推荐的额外Lint规则
linter:
rules:
# Material Design 3相关
use_colored_box: true
use_decorated_box: true
# 性能相关
avoid_unnecessary_containers: true
sized_box_for_whitespace: true
# 代码质量
prefer_const_constructors: true
prefer_const_literals_to_create_immutables: true
prefer_const_declarations: true
# 新增的推荐规则
use_super_parameters: true
unnecessary_late: true
analyzer:
exclude:
- "**/*.g.dart"
- "**/*.freezed.dart"
步骤 4:执行依赖更新
1
2
3
4
5
6
7
8
9
# 获取新的依赖版本
flutter pub get
# 如果有依赖冲突,尝试升级
flutter pub upgrade
# 清理并重新获取依赖
flutter clean
flutter pub get
主要破坏性变更处理
1. ThemeData 变更
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Flutter 2写法
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
// 旧的属性可能已弃用
accentColor: Colors.blueAccent,
),
home: HomePage(),
);
}
}
// Flutter 3推荐写法
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
// 使用新的颜色系统
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.light,
),
// 采用Material 3设计
useMaterial3: true,
),
home: const HomePage(),
);
}
}
2. AppBar 样式更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Flutter 2写法
AppBar(
backgroundColor: Colors.blue,
elevation: 4.0,
title: Text('My App'),
centerTitle: true,
)
// Flutter 3写法 - Material 3风格
AppBar(
title: const Text('My App'),
centerTitle: true,
// Material 3会自动应用新的样式
// 如需自定义,使用AppBarTheme
)
// 在ThemeData中自定义AppBar
ThemeData(
useMaterial3: true,
appBarTheme: const AppBarTheme(
centerTitle: true,
elevation: 0,
scrolledUnderElevation: 4,
),
)
3. 文本样式更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Flutter 2写法
Text(
'Hello World',
style: Theme.of(context).textTheme.headline6,
)
// Flutter 3写法 - 新的文本样式命名
Text(
'Hello World',
style: Theme.of(context).textTheme.titleLarge, // headline6 -> titleLarge
)
// 完整的文本样式映射
class TextStyleHelper {
static TextStyle? getHeadline1(BuildContext context) {
return Theme.of(context).textTheme.displayLarge; // headline1 -> displayLarge
}
static TextStyle? getHeadline2(BuildContext context) {
return Theme.of(context).textTheme.displayMedium; // headline2 -> displayMedium
}
static TextStyle? getHeadline3(BuildContext context) {
return Theme.of(context).textTheme.displaySmall; // headline3 -> displaySmall
}
static TextStyle? getHeadline4(BuildContext context) {
return Theme.of(context).textTheme.headlineMedium; // headline4 -> headlineMedium
}
static TextStyle? getHeadline5(BuildContext context) {
return Theme.of(context).textTheme.headlineSmall; // headline5 -> headlineSmall
}
static TextStyle? getHeadline6(BuildContext context) {
return Theme.of(context).textTheme.titleLarge; // headline6 -> titleLarge
}
}
4. 按钮样式更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Flutter 2写法
RaisedButton(
onPressed: () {},
child: Text('Press Me'),
color: Colors.blue,
)
FlatButton(
onPressed: () {},
child: Text('Flat Button'),
)
// Flutter 3写法 - 使用新的按钮组件
ElevatedButton(
onPressed: () {},
child: const Text('Press Me'),
)
TextButton(
onPressed: () {},
child: const Text('Text Button'),
)
OutlinedButton(
onPressed: () {},
child: const Text('Outlined Button'),
)
5. 导航相关更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Flutter 2写法
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => SecondPage()),
);
// Flutter 3推荐写法 - 使用GoRouter
// pubspec.yaml中添加: go_router: ^5.0.0
import 'package:go_router/go_router.dart';
final GoRouter _router = GoRouter(
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return const HomePage();
},
routes: <RouteBase>[
GoRoute(
path: '/details',
builder: (BuildContext context, GoRouterState state) {
return const DetailsPage();
},
),
],
),
],
);
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: _router,
);
}
}
// 导航使用
context.go('/details');
iOS 平台特定注意事项
1. 更新 iOS 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- ios/Runner/Info.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- 确保最低iOS版本支持 -->
<key>MinimumOSVersion</key>
<string>11.0</string>
<!-- Flutter 3需要的新权限配置 -->
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to take photos</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs photo library access</string>
</dict>
</plist>
2. 更新 Podfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# ios/Podfile
platform :ios, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
# Flutter 3兼容性设置
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
'PERMISSION_CAMERA=1',
'PERMISSION_PHOTOS=1',
]
end
end
end
3. iOS 构建脚本更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
# build_ios.sh
echo "开始iOS构建流程..."
# 清理之前的构建
flutter clean
cd ios && rm -rf Pods Podfile.lock && cd ..
# 获取依赖
flutter pub get
# 安装iOS依赖
cd ios && pod install && cd ..
# 构建iOS应用
flutter build ios --release
echo "iOS构建完成!"
性能优化和新特性应用
1. Material 3 设计语言应用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class ModernCard extends StatelessWidget {
final String title;
final String subtitle;
final VoidCallback? onTap;
const ModernCard({
Key? key,
required this.title,
required this.subtitle,
this.onTap,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
// Material 3的新特性
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Theme.of(context).colorScheme.surfaceTint,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 8),
Text(
subtitle,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
],
),
),
),
);
}
}
2. 新的导航栏组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class ModernBottomNavigation extends StatefulWidget {
@override
_ModernBottomNavigationState createState() => _ModernBottomNavigationState();
}
class _ModernBottomNavigationState extends State<ModernBottomNavigation> {
int _selectedIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: _getPage(_selectedIndex),
bottomNavigationBar: NavigationBar(
selectedIndex: _selectedIndex,
onDestinationSelected: (index) {
setState(() {
_selectedIndex = index;
});
},
destinations: const [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: '首页',
),
NavigationDestination(
icon: Icon(Icons.search_outlined),
selectedIcon: Icon(Icons.search),
label: '搜索',
),
NavigationDestination(
icon: Icon(Icons.favorite_outline),
selectedIcon: Icon(Icons.favorite),
label: '收藏',
),
NavigationDestination(
icon: Icon(Icons.person_outline),
selectedIcon: Icon(Icons.person),
label: '我的',
),
],
),
);
}
Widget _getPage(int index) {
switch (index) {
case 0:
return const HomePage();
case 1:
return const SearchPage();
case 2:
return const FavoritePage();
case 3:
return const ProfilePage();
default:
return const HomePage();
}
}
}
3. 性能优化最佳实践
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 使用const构造函数优化性能
class OptimizedWidget extends StatelessWidget {
const OptimizedWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: const [
// 尽可能使用const
Text('Static Text'),
SizedBox(height: 16),
Icon(Icons.star),
],
);
}
}
// 使用RepaintBoundary优化重绘
class ExpensiveWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RepaintBoundary(
child: CustomPaint(
painter: ComplexPainter(),
size: Size(200, 200),
),
);
}
}
// 使用AutomaticKeepAliveClientMixin保持状态
class KeepAliveWidget extends StatefulWidget {
@override
_KeepAliveWidgetState createState() => _KeepAliveWidgetState();
}
class _KeepAliveWidgetState extends State<KeepAliveWidget>
with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
super.build(context); // 必须调用
return const Text('This widget will be kept alive');
}
}
常见问题和解决方案
1. 依赖冲突解决
1
2
3
4
5
6
7
8
9
10
# 问题:依赖版本冲突
# 解决方案:
flutter pub deps
flutter pub upgrade --major-versions
# 如果仍有冲突,手动指定版本
# pubspec.yaml
dependency_overrides:
meta: ^1.8.0
collection: ^1.16.0
2. iOS 构建错误
1
2
3
4
5
6
7
8
9
# 问题:iOS构建失败
# 解决方案:
cd ios
rm -rf Pods Podfile.lock
pod repo update
pod install
cd ..
flutter clean
flutter build ios
3. Material 3 兼容性问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 问题:某些组件在Material 3下显示异常
// 解决方案:逐步迁移,保持兼容性
class CompatibleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
// 可以选择性启用Material 3
useMaterial3: true,
// 自定义主题确保兼容性
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
elevation: 2,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
),
),
),
home: const HomePage(),
);
}
}
4. 调试工具使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 性能调试
class PerformanceDebugging {
static void enablePerformanceOverlay() {
// 在MaterialApp中添加
// debugShowMaterialGrid: true,
// showPerformanceOverlay: true,
}
static void profileWidgetBuilds() {
// 使用Flutter Inspector
// flutter pub global activate devtools
// flutter pub global run devtools
}
}
5. 内存优化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class MemoryOptimizedImageWidget extends StatelessWidget {
final String imageUrl;
const MemoryOptimizedImageWidget({
Key? key,
required this.imageUrl,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Image.network(
imageUrl,
// 内存优化配置
cacheWidth: 400,
cacheHeight: 400,
filterQuality: FilterQuality.medium,
errorBuilder: (context, error, stackTrace) {
return const Icon(Icons.error);
},
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return const CircularProgressIndicator();
},
);
}
}
升级后的验证项
功能验证
- 应用正常启动
- 所有页面正常显示
- 导航功能正常
- 网络请求正常
- 本地存储功能正常
- 推送通知正常
性能验证
- 启动时间没有明显延长
- 内存使用正常
- 动画流畅度良好
- 列表滚动性能正常
兼容性验证
- iOS 不同版本兼容性
- 不同屏幕尺寸适配
- 深色模式正常
- 辅助功能支持
构建验证
- Debug 构建正常
- Release 构建正常
- 打包大小合理
- 签名和发布正常
总结
升级过程比较繁琐,需要保持耐心,注意一些 API 的更新和使用。
This post is licensed under CC BY 4.0 by the author.