Flutter woo 实战课程升级 sdk v3.3 版本

2022-09-20

视频

bilibiliopen in new window

课程

2022 Flutter 3 Getx Woo 电商 APP 实战课open in new window

前言

不少群友都在问还没有适配最新的 sdk v3.3 么,我们要学习最新的版本。

其实本人对这些版本并不敏感,而且很保守,我是在等待有个稳定的小版本再去升级,这样生态也会更加稳定。

那就升级下吧。

下面说下这次从 v2.10.5 -> v3.3 遇到的体会和问题。

v3 为我们带来了什么

正文 开始升级 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 中使用库私有类型。

  • 修改前
  
  _TextFormWidgetState createState() => _TextFormWidgetState();
}

class _TextFormWidgetState extends State<TextFormWidget> {
  // 是否显示明文按钮
  bool _isShowObscureIcon = false;

  • 修改后
  
  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 扩展了样式属性

官方提供了一个优雅的方式来扩展样式

    1. 定义扩展

我这里定义了两套颜色配置

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,'
        ')';
  }
}
    1. 编写 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 来设计。

md3 theme builderopen in new window

    1. 注册扩展

lib/main.dart

...
          child: GetMaterialApp(
            title: 'Flutter Demo',

            // 样式
            theme:
                ConfigService.to.isDarkModel ? AppTheme.dark : AppTheme.light,

这里并没有什么特别的,和原来一样。

    1. 访问扩展

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

Last Updated:
Contributors: ducafecat