Flutter问题记录 - Flutter插件创建Package项目报错:At least one platform must be selected

文章讲述了作者遇到的在使用Flutter插件创建Package项目时遇到的问题,即平台选择选项灰色不可选,导致创建失败。作者通过问题分析,发现是Flutter插件的版本更新导致的,具体是由于某个版本中移除的特定判断导致。文章提供了问题的解决方案,包括等待官方修复或降级Flutter插件版本,以及使用命令行工具创建Package项目。
摘要由CSDN通过智能技术生成


前言

大概一个多月前我遇到了这个问题,当时搜了搜发现在2021年出现过这个问题,不过已经修复了,所以现在这个肯定又是一个新问题。当时简单折腾了一下,发现解决不了便先通过Flutter命令行工具创建Package项目,忙着工作忘了提个issue。

时隔一个多月,更新了Flutter和Flutter插件,创建Package项目发现问题竟然还在,不过已经有人提了issue,这个问题的修复计划已经添加到了里程碑,预计在72版本修复。

2023/02/05更新:新发布的72.0版本已经修复该问题,涉及该问题修复的PR。如果你只是想快速解决问题,升级Flutter插件版本即可。如果你对产生问题的原因感兴趣,欢迎阅读以下内容。

开发环境

  • Android Studio: 2021.3.1 Patch 1
  • Flutter: 3.3.10
  • Flutter插件: 71.2.3

问题描述

通过Flutter插件创建Package项目,点击"Finish"按钮后创建Package项目失败并弹框报错:

screenshot1

根据报错信息提示需要选择platform,但是现在是置灰的选择不了:

screenshot2

正常情况下应该是这样的:

screenshot3

问题分析

首先怀疑是不是Flutter插件版本没更新导致的问题,检查发现已经是最新版本,卸载重装Flutter插件问题依旧还在。接着尝试升级Flutter版本,从3.3.5版本升到当时最新的3.3.8版本,问题依旧存在。

试了试在同事电脑上创建Package项目,一切正常。对比两台电脑的Flutter插件版本,我电脑上的版本是71.0.3,同事电脑上的版本是70.0.2,这两个版本有什么区别呢?

Flutter插件版本列表部分截图:

screenshot4

从上图中可以看出,Flutter插件版本主要看前两个数字,最后一个数字应该是用于兼容不同的Android Studio(IDEA)版本,不同的IDEA版本有不同的JDK版本等要求。通过Flutter插件的版本说明也可以看出,插件版本看前两个数字。

screenshot5

现在两个插件版本除了大版本不一样,用于兼容处理的版本也不一样,所以可以初步确定,Flutter插件的问题不是出在功能更新上,就是Android Studio(IDEA)版本兼容出了问题,不过后者的可能性比较低。

先排查是不是Flutter插件功能更新引起的问题,把Flutter插件仓库代码拉到本地,搜索关键词At least one platform must be selected定位报错位置:

screenshot6

继续搜索关键词npw_none_selected_error

screenshot7

报错位置找到了,继续搜索关键词isSomePlatformSelected查看方法定义:

screenshot8

isSomePlatformSelected的方法定义可以看出,如果一个平台都没选择会返回false从而导致报错。可问题是,创建Package项目时支持的平台选项都置灰了,根本没办法选择。这块代码在70版本到71版本之间极有可能做了改动,现在需要定位Git历史提交记录看做了什么改动。[选中settings.isSomePlatformSelected()] -> [右击选择Git] -> [Show History for Selection],通过这个操作可以快速定位这部分代码涉及的Git历史提交记录。

screenshot9

settings.isSomePlatformSelected()代码涉及的Git历史提交记录:

screenshot10

可以看到70版本到71版本之间有一个在10月28号的提交记录,继续往下查看这个提交改动了什么。

screenshot11

比较代码可以看到移除了isShowingPlatforms判断,这个方法在当前插件项目中已经移除,所以搜索不到,不过我们可以查看这个提交记录的全部改动。

screenshot12

isShowingPlatforms的值由projectTypeHasPlatformsplatformsForm.shouldBeVisible决定,projectTypeHasPlatforms的定义看下图:

screenshot13

从图中被移除的代码可以看出,如果项目类型为Package会返回false,也就是说70版本的时候这个逻辑是没问题的,71版本少了这个判断导致在validateSettings方法校验设置的时候报错。至于platformsForm.shouldBeVisible这个方法的移除应该不是导致这个问题的原因,platformsForm.shouldBeVisible的定义如下:

screenshot14

图中ch.id > ID.STABLE || sdkGetter.get().version.stableChannelSupportsPlatforms()代码的作用是如果当前Flutter SDK渠道不是稳定版直接返回true,也就是创建项目时支持的平台是可见的,如果当前是稳定版,则需要判断一下当前版本是否大于最大不支持平台选择的稳定版本。简单来说就是Flutter在1.22.6版本以后才支持平台选择,所以需要做兼容判断。

查看这个提交记录的过程,我发现了71版本平台选项置灰不可选择的原因。

screenshot15

areLanguageFeaturesVisible的定义:

boolean areLanguageFeaturesVisible = projectType != FlutterProjectType.PACKAGE && projectType != FlutterProjectType.MODULE;

71版本之前,isShowingPlatforms为true才设置平台表单组件是否可选择,这就导致创建项目类型为PACKAGEMODULE的项目时默认可以选择平台,但这个选择是不生效的。在Flutter插件项目中FlutterCreateAdditionalSettingsFields类中的getAdditionalSettings方法对平台选项做了过滤,不符合项目类型的平台相关参数会被设为null
PACKAGEMODULE这两个项目类型选择平台不生效的原因有点不同,PACKAGE项目是纯dart项目,不区分平台,MODULE项目默认只支持Android和iOS两个平台,无法自定义,感兴趣的可以翻一翻Flutter关于命令行创建项目的代码

Flutter关于命令行创建项目的部分代码:

screenshot16

71版本改动后,平台表单组件可不可以选择完全由areLanguageFeaturesVisible属性决定。

到此,问题分析完了,后续的修复很简单就不赘述,感兴趣的可以看看我提的这个PR

解决方案

最好的方案就是等待Flutter插件问题修复后更新,如果你急着创建Package项目,这边提供两种解决方案:

  1. Flutter插件降版本

前往Flutter插件版本列表下载70.2.X版本,具体的X请对照自己安装的Android Studio(IDEA)版本,例如Android Studio 2021.3.1 版本对应的插件版本是70.2.3。下载完成后,如果系统自动对压缩文件做了解压处理并移到了废纸篓,需要将移到废纸篓的flutter-intellij.zip文件放回原处,这是因为Android Studio安装插件时需要插件的压缩文件,选择解压后的文件是无法安装成功的。

Android Studio插件安装步骤请看下图:

screenshot17

如果已经安装有最新版本的Flutter插件,不用先卸载,Android Studio支持覆盖安装插件。安装成功后可以看到版本变为了70.2.3:

screenshot18

这时就暂时别升级版本了,等问题修复后再升级。

  1. Flutter命令行工具创建Package项目

虽然Flutter插件创建Package项目有问题,但是命令行工具是没问题的。创建Package项目是个低频操作,完全可以先通过命令行工具临时解决。

先看看Flutter插件正常创建Package项目时需要什么参数:

screenshot3

可以看到创建一个Package项目需要五个参数,一是项目名称,二是项目路径,三是项目描述,四是项目类型,五是支持的平台,根据前面的问题分析,支持的平台参数是不需要的。

再来看看Flutter创建命令有什么参数可以用:

flutter help create

执行输出(省略部分):

Usage: flutter create <output directory>
-h, --help                   Print this usage information.
    --[no-]pub               Whether to run "flutter pub get" after the project
                             has been created.
                             (defaults to on)
    --[no-]offline           When "flutter pub get" is run by the create
                             command, this indicates whether to run it in
                             offline mode or not. In offline mode, it will need
                             to have all dependencies already available in the
                             pub cache to succeed.
    --[no-]overwrite         When performing operations, overwrite existing
                             files.
    --description            The description to use for your new Flutter
                             project. This string ends up in the pubspec.yaml
                             file.
                             (defaults to "A new Flutter project.")
    --org                    The organization responsible for your new Flutter
                             project, in reverse domain name notation. This
                             string is used in Java package names and as prefix
                             in the iOS bundle identifier.
                             (defaults to "com.example")
    --project-name           The project name for this new Flutter project. This
                             must be a valid dart package name.
-i, --ios-language           The language to use for iOS-specific code, either
                             Objective-C (legacy) or Swift (recommended).
                             [objc, swift (default)]
-a, --android-language       The language to use for Android-specific code,
                             either Java (legacy) or Kotlin (recommended).
                             [java, kotlin (default)]
    --platforms              The platforms supported by this project. Platform
                             folders (e.g. android/) will be generated in the
                             target project. This argument only works when
                             "--template" is set to app or plugin. When adding
                             platforms to a plugin project, the pubspec.yaml
                             will be updated with the requested platform. Adding
                             desktop platforms requires the corresponding
                             desktop config setting to be enabled.
                             [ios (default), android (default), windows
                             (default), linux (default), macos (default), web
                             (default)]
-t, --template=<type>        Specify the type of project to create.

          [app]              (default) Generate a Flutter application.
          [module]           Generate a project to add a Flutter module to an
                             existing Android or iOS application.
          [package]          Generate a shareable Flutter project containing
                             modular Dart code.
          [plugin]           Generate a shareable Flutter project containing an
                             API in Dart code with a platform-specific
                             implementation through method channels for Android,
                             iOS, Linux, macOS, Windows, web, or any combination
                             of these.
          [plugin_ffi]       Generate a shareable Flutter project containing an
                             API in Dart code with a platform-specific
                             implementation through dart:ffi for Android, iOS,
                             Linux, macOS, Windows, or any combination of these.
          [skeleton]         Generate a List View / Detail View Flutter
                             application that follows community best practices.

-s, --sample=<id>            Specifies the Flutter code sample to use as the
                             "main.dart" for an application. Implies
                             "--template=app". The value should be the sample ID
                             of the desired sample from the API documentation
                             website (http://docs.flutter.dev/). An example can
                             be found at:
                             https://api.flutter.dev/flutter/widgets/SingleChild
                             ScrollView-class.html
    --list-samples=<path>    Specifies a JSON output file for a listing of
                             Flutter code samples that can be created with
                             "--sample".

可以看到flutter create后面跟的占位参数<output directory>就是用来指定项目路径的,项目名称通过--project-name指定,项目描述通过--description指定,项目类型通过-t--template指定。到此,创建Package项目的参数已经集齐,创建命令格式如下:

flutter create 项目路径 --project-name 项目名称 --description 项目描述 -t package

项目描述不是很重要而且后面可以随时在pubspec.yaml文件内修改,所以可以暂时不指定:

flutter create 项目路径 --project-name 项目名称 -t package

到此,第二种方案写完啦。以下是一些不太重要的探索,不感兴趣的可以略过。

根据问题分析中对Flutter插件项目的理解,创建项目类型为PACKAGEMODULE的项目时,选择支持的平台是无效的。那Flutter创建命令是这样的吗?查看--platforms的描述里面说明了只有项目类型指定为appplugin时才有效(描述有点小问题,后面增加的plugin_ffiskeleton也是支持选择平台,在Flutter 3.6版本会增加-e, --[no-]empty参数用于指定EMPTY项目类型,这个也支持选择平台),现在可以确定Flutter创建命令也是这样的。如果创建Package项目时指定了--platforms会报以下错误:

The "--platforms" argument is not supported in package template.

通过以上报错信息,我们可以定位到前面提到的Flutter关于命令行创建项目的代码

screenshot19

再通过查找报错信息涉及的Git历史提交记录,可以看到这段报错信息是在Flutter plugin项目创建支持指定--platforms时添加的,于2020年6月提交。

screenshot20

那Flutter插件关于平台是否可选择的代码是在什么时候添加的呢?

screenshot21

从历史提交记录可以看到,是在2021年2月添加的。在2月19号首次提交时,平台表单组件可不可以选择完全由areLanguageFeaturesVisible属性决定,在2月24号不知道因为什么原因加上了isShowingPlatforms判断,一直持续到了2022年10月28号。Flutter插件支持平台选择的提交记录可以追溯到2020年12月,从支持平台选择开始到平台不可以选择(置灰),这段期间创建Package项目时指定的平台都是无效的。

可能有人会有点疑惑,在Pub仓库能看到一些Package类型的库显示只支持某些平台,不是说Package项目创建时指定平台无效吗?关于这个,感兴趣的请看这篇文章Flutter & Dart - Package类型项目的平台支持

总结

写这篇文章初衷只是想简单记录一下这个问题,结果在梳理问题的过程中越写越多,谁能想到就这么一个小问题牵扯的东西一点也不少。本来元旦写的差不多了,结果忙忘记了一直没发。

最后

如果这篇文章对你有所帮助,请不要吝啬你的点赞👍加星🌟,谢谢~

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值