Flutter woo 实战课程升级 sdk v3.3 版本
2022-09-20
视频
课程
2022 Flutter 3 Getx Woo 电商 APP 实战课
前言
不少群友都在问还没有适配最新的 sdk v3.3 么,我们要学习最新的版本。
其实本人对这些版本并不敏感,而且很保守,我是在等待有个稳定的小版本再去升级,这样生态也会更加稳定。
那就升级下吧。
下面说下这次从 v2.10.5 -> v3.3 遇到的体会和问题。
v3 为我们带来了什么
参考
重要以下几点
SelectableArea
多行文本选择- 触控板输入 支持
Scribble
手写组件支持- 更好的富文本支持 rich_text_editor
- VSCode DevTools 的升级 (感觉新版不兼容 v2 的 sdk)
- Material Design 3 正式支持
- go_router 路由管理
- 性能进一步提升 减少
GC
带来的消耗 - iOS 指针压缩已禁用 提升稳定性
- 异常捕捉
PlatformDispatcher.onError
- 32 位 iOS 弃用
- Bitcode deprecation 放弃对位码的支持
正文 开始升级 v3
代码直接能在 v3.3 下顺利运行
我本机是用 fvm 管理的,所以很方便的在多个版本中切换。
- 我的顺序如下:
- 用
flutter crate
创建一个 v3 的空项目 - 将
common pages
代码复制过去 - 将
pubspec.yaml
中的版本都改成指定版本get: 4.6.5
,将^
去掉
- 用
指定版本的好处就是,有些小版本的升级可能会造成你无法运行。
然后我就顺利运行起来了,毫无问题。
修正了很多的警告
DO NOT use BuildContext across asynchronous gaps.
https://dart-lang.github.io/linter/lints/use_build_context_synchronously.html
不能跨异步执行,有安全漏洞,我把操作拆开
- 原来的代码
/// 相册 assets
static Future<List<AssetEntity>?> assets({
required BuildContext context,
List<AssetEntity>? selected,
RequestType type = RequestType.image,
int maxAssets = 9,
SpecialPickerType? specialPickerType,
Widget? Function(BuildContext, AssetPathEntity?, int)? specialItemBuilder,
SpecialItemPosition specialItemPosition = SpecialItemPosition.none,
}) async {
var privilege = await Privilege.photos();
if (!privilege.result) {
await ActionDialog.normal(
context: context,
content: Text(privilege.message),
confirm: const Text('Setting'),
cancel: const Text('Not allowed'),
onConfirm: () => Privilege.openSettings(),
);
return null;
}
var result = await AssetPicker.pickAssets(
context,
pickerConfig: AssetPickerConfig(
selectedAssets: selected,
requestType: type,
maxAssets: maxAssets,
themeColor: AppColors.surfaceVariant,
specialPickerType: specialPickerType,
filterOptions: FilterOptionGroup(
orders: [const OrderOption(type: OrderOptionType.createDate)],
videoOption: const FilterOption(
durationConstraint: DurationConstraint(
min: Duration(seconds: videoDurationMin),
max: Duration(seconds: videoDurationMax),
),
),
),
specialItemPosition: specialItemPosition,
specialItemBuilder: specialItemBuilder,
),
);
return result;
}
- 修改后
lib/common/utils/picker.dart
/// 相机 权限
static Future<PrivilegeStatus> cameraPrivilege(BuildContext context) async {
var privilege = await Privilege.camera();
if (!privilege.result) {
await ActionDialog.normal(
context: context,
content: Text(privilege.message),
confirm: const Text('Setting'),
cancel: const Text('Not allowed'),
onConfirm: () => Privilege.openSettings(),
);
return PrivilegeStatus(result: false, message: "");
}
return privilege;
}
/// 相机
static Future<AssetEntity?> camera({
required BuildContext context,
bool enableRecording = true,
}) async {
var result = await CameraPicker.pickFromCamera(
context,
pickerConfig: CameraPickerConfig(
enableRecording: enableRecording,
enableAudio: enableRecording,
textDelegate:
enableRecording ? const EnglishCameraPickerTextDelegate() : null,
resolutionPreset: ResolutionPreset.veryHigh,
),
);
return result;
}
lib/common/utils/picker_image.dart
: () async {
// 相机权限
var privilege =
await ActionPicker.cameraPrivilege(Get.context!);
if (!privilege.result) {
return;
}
// 相机拍摄
var result = await ActionPicker.camera(
context: Get.context!,
enableRecording: false,
);
onTapTake!(result);
Get.back();
},
Sort child properties last in widget instance creations.
https://dart-lang.github.io/linter/lints/sort_child_properties_last.html
child 属性排到最后
- 修改前
/// 背景图片
Widget backgroundImage(
DecorationImage image, {
Key? key,
}) =>
DecoratedBox(
key: key,
child: this,
decoration: BoxDecoration(image: image),
);
- 修改后
/// 背景图片
...
=>
DecoratedBox(
key: key,
decoration: BoxDecoration(image: image),
child: this, // .... 移动到最后
);
Avoid leading underscores for local identifiers.
https://dart-lang.github.io/linter/lints/no_leading_underscores_for_local_identifiers.html
局部参数不要用下划线开头
- 修改前
int? _maxHeight = maxHeight?.toInt();
- 修改后
int? imgMaxHeight = maxHeight?.toInt();
Avoid using private types in public APIs.
https://dart-lang.github.io/linter/lints/library_private_types_in_public_api.html
避免在公共 API 中使用库私有类型。
- 修改前
createState() => _TextFormWidgetState();
}
class _TextFormWidgetState extends State<TextFormWidget> {
// 是否显示明文按钮
bool _isShowObscureIcon = false;
_TextFormWidgetState
- 修改后
State<TextFormWidget> createState() => _TextFormWidgetState();
Use interpolation to compose strings and values.
https://dart-lang.github.io/linter/lints/prefer_interpolation_to_compose_strings.html
合并字符串表达式
- 修改前
"debounce -> " + value.toString()
- 修改后
"debounce -> $value"
采用了 ThemeExtensions 扩展了样式属性
官方提供了一个优雅的方式来扩展样式
- 定义扩展
我这里定义了两套颜色配置
lib/common/style/theme_extension/ext_base.dart
import 'package:flutter/material.dart';
/// 基础功能色
class ExtBaseColorTheme extends ThemeExtension<ExtBaseColorTheme> {
/// 亮
static const light = ExtBaseColorTheme(
info: Color(0xFF2196F3),
success: Color(0xFF4CAF50),
warning: Color(0xFFFFC107),
danger: Color(0xFFF44336),
);
/// 暗
static const dark = ExtBaseColorTheme(
info: Color(0xFF2196F3),
success: Color(0xFF4CAF50),
warning: Color(0xFFFFC107),
danger: Color(0xFFF44336),
);
/// 信息
final Color? info;
/// 成功
final Color? success;
/// 警告
final Color? warning;
/// 错误
final Color? danger;
const ExtBaseColorTheme({
required this.info,
required this.success,
required this.warning,
required this.danger,
});
ThemeExtension<ExtBaseColorTheme> copyWith({
Color? info,
Color? success,
Color? warning,
Color? error,
}) {
return ExtBaseColorTheme(
info: info ?? this.info,
success: success ?? this.success,
warning: warning ?? this.warning,
danger: error ?? danger,
);
}
ThemeExtension<ExtBaseColorTheme> lerp(
ThemeExtension<ExtBaseColorTheme>? other, double t) {
if (other is! ExtBaseColorTheme) {
return this;
}
return ExtBaseColorTheme(
info: Color.lerp(info, other.info, t),
success: Color.lerp(success, other.success, t),
warning: Color.lerp(warning, other.warning, t),
danger: Color.lerp(danger, other.danger, t),
);
}
String toString() {
return 'ExtBaseColorTheme('
'info: $info,'
'success: $success,'
'warning: $warning,'
'error: $danger,'
')';
}
}
lib/common/style/theme_extension/ext_woo.dart
import 'package:flutter/material.dart';
/// 产品色
class ExtWooColorTheme extends ThemeExtension<ExtWooColorTheme> {
static const light = ExtWooColorTheme(highlight: Color(0xFFF77866));
static const dark = ExtWooColorTheme(highlight: Color(0xFFFFB4A9));
/// 强调色
final Color? highlight;
const ExtWooColorTheme({
required this.highlight,
});
ThemeExtension<ExtWooColorTheme> copyWith({
Color? highlight,
}) {
return ExtWooColorTheme(
highlight: highlight ?? this.highlight,
);
}
ThemeExtension<ExtWooColorTheme> lerp(
ThemeExtension<ExtWooColorTheme>? other, double t) {
if (other is! ExtWooColorTheme) {
return this;
}
return ExtWooColorTheme(
highlight: Color.lerp(highlight, other.highlight, t),
);
}
String toString() {
return 'ExtWooColorTheme('
'highlight: $highlight,'
')';
}
}
- 编写 ThemeData 定义
lib/common/style/theme.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'index.dart';
/// 主题
class AppTheme {
/// 亮色
static ThemeData light = ThemeData(
useMaterial3: false,
colorScheme: lightColorScheme,
extensions: const <ThemeExtension<dynamic>>[
ExtBaseColorTheme.light,
ExtWooColorTheme.light,
],
fontFamily: "Montserrat",
appBarTheme: const AppBarTheme(
...
),
);
/// 暗色
static ThemeData dark = ThemeData(
useMaterial3: false,
colorScheme: darkColorScheme,
extensions: const <ThemeExtension<dynamic>>[
ExtBaseColorTheme.dark,
ExtWooColorTheme.dark,
],
fontFamily: "Montserrat",
appBarTheme: const AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle.light, // appBar 亮色 , 和主题色相反
),
);
}
注意这个 extensions
字段,数组类型。这里可以添加多个扩展,然后在后面的代码中就可以直接使用了。
useMaterial3: false,
可以发现我这里是关闭的,如果不关你的组件配色会很奇怪,毕竟我们没有按 md3 来设计。
- 注册扩展
lib/main.dart
...
child: GetMaterialApp(
title: 'Flutter Demo',
// 样式
theme:
ConfigService.to.isDarkModel ? AppTheme.dark : AppTheme.light,
这里并没有什么特别的,和原来一样。
- 访问扩展
lib/common/style/colors.dart
/// 应用颜色
class AppColors {
/// *******************************************
/// 自定义 颜色
/// *******************************************
/// 产品
static ExtWooColorTheme get woo => Get.theme.extension<ExtWooColorTheme>()!;
static Color get highlight =>
Get.theme.extension<ExtWooColorTheme>()!.highlight!;
/// 基础色
static ExtBaseColorTheme get base =>
Get.theme.extension<ExtBaseColorTheme>()!;
static Color get info => Get.theme.extension<ExtBaseColorTheme>()!.info!;
static Color get success =>
Get.theme.extension<ExtBaseColorTheme>()!.success!;
static Color get warning =>
Get.theme.extension<ExtBaseColorTheme>()!.warning!;
static Color get danger => Get.theme.extension<ExtBaseColorTheme>()!.danger!;
通过 context
的方式读取 theme.extension<ExtBaseColorTheme>()!.info!
我这里肯定有 Get.context
而且都有默认值不会空,所以加入了空安全 !
符号。
总结
- 提示几点:
- v2 代码到 v3 能直接运行兼容性不错
- v2 代码请把
useMaterial3
开关 false ThemeExtension
样式属性扩展可以用起来- 编辑器有警告的还是修复下
end