谷歌 Flutter 移动开发快速启动指南(二)

原文:zh.annas-archive.org/md5/2ff865f719115aa08fbcf9d0c2b760d8

译者:飞龙

协议:CC BY-NC-SA 4.0

第七章:Firebase - Flutter 的最佳伙伴

使用Firebase构建应用是世界上增长最快的科技趋势之一。使用 Firebase,开发者可以快速构建应用,无需管理基础设施,包括身份验证、存储和同步数据、安全托管 Web 资产和云存储。Firebase 有一个免费的基础计划,提供 1 GB 的存储空间和 100 个并发连接。如果您想升级,您可以在此查看计划:firebase.google.com/pricing/

在本章中,我们将涵盖以下主题:

  • 与 Firebase 连接

  • 创建 Cloud Firestore 数据库

  • Firebase 云消息传递

  • Firebase 远程配置

与 Firebase 连接

首先,让我们看看如何连接到 Firebase。我们首先需要确保与 Firebase 的连接是否正确建立;为此,请按照以下步骤操作:

  1. 在您的 IDE 或编辑器中创建一个新的 Flutter 项目

  2. 打开pubspec.yaml文件

  3. 添加以下依赖项:

dependencies:
  flutter:
    sdk: flutter

  cloud_firestore:.9.5+2  //Add this line

有关 Flutter Cloud Firestore 插件的最新版本详情,请访问 Pub 网站:pub.dartlang.org/packages/cloud_firestore

  1. 接下来,为了建立连接,在您的 IDE 或使用命令行运行以下命令:
flutter packages get

创建 Firebase 项目

一旦建立连接,下一步就是创建一个 Firebase 项目。那么,让我们开始吧。按照以下步骤创建您的项目:

  1. 打开网站firebase.google.com并登录或注册。您可以使用您的 Google 凭证在此处登录。

  2. 接下来,点击添加项目。

  3. 一旦点击此选项,您将看到以下屏幕:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/b04f4a22-a72d-44a7-b6d8-f5a196155b8e.png

  1. 添加项目名称(例如:在我们的案例中为Firebase Flutter Demo App)。项目 ID会自动生成,或者您可以输入您自己的唯一项目 ID。它们是全球唯一的标识符。

  2. 在位置中选择国家,然后点击创建项目**。**之前接受条款和条件。

  3. 点击创建项目选项,等待几秒钟,直到 Firebase 控制台显示以下截图中的消息:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/38a93117-ba5f-4bb5-8838-54eef11a6727.png

  1. 如果您的屏幕显示您的项目已准备好文本,如前一张截图所示,您可以点击继续按钮。

  2. 完成后,您将看到应用的以下项目设置仪表板:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/28e7ff8a-a759-44d1-917d-f019ee7c78ef.png

  1. 根据您将为哪个应用平台构建应用选择平台特定的 Firebase 配置,并点击相应的图标。在我们的案例中,因为我们正在构建 Android 应用,所以我们将点击 Android 图标继续。

使用包名注册应用

此步骤需要将您应用的平台特定 ID 注册到 Firebase。这将生成我们将添加到项目文件夹中的配置文件。请注意,在您的 Flutter 应用顶级目录中,iOS 和 Android 是包含相应平台特定配置文件的子目录之一:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/66815961-af58-4e30-ae9e-39f3b71315bd.png

在您的 Flutter 应用顶级目录中,您可以看到子目录;称为 Android 和 iOS。在这里,您将找到 iOS 和 Android 的平台特定配置文件。

在这里最重要的字段是 Android 包名**。**这通常是您 app-level build.gradle文件中的applicationId。另一种找到包名的方法是按照以下步骤操作:

  1. 在 Flutter 应用目录中,检查android/app/src/main/AndroidManifest.xml文件。

  2. 在 Manifest 标签下,找到包名的字符串值,这将是包名的值。

  3. 在 Firebase 对话框中,将步骤 2 中复制的包名粘贴到 Android 包名字段中。

如果您正在为 iOS 和 Android 两个平台开发 Flutter 应用,您需要在同一个 Firebase 项目中注册 iOS 和 Android 版本。但如果您只为一个平台开发,您只需点击其中一个即可。

接下来,您可以添加应用昵称,这是一个可选字段。还有一个可选字段调试签名证书 SHA-1,如果应用使用如 Google Sign in 进行身份验证、Dynamic Links 和 Invites 等功能,则必须使用。在这种情况下,您必须找到调试证书指纹值,并将其复制粘贴到字段中。有关如何构建客户端身份验证的说明,请参阅此链接,developers.google.com/android/guides/client-auth。由于在此示例中我们没有使用这些功能,我们将留空。点击注册应用。

下载和设置配置文件

下一步是下载并设置配置文件。按照以下步骤下载并设置配置文件:

  1. 点击注册应用后,此步骤中的控制台将生成google-services.json文件。将此文件下载到您的计算机上。

  2. 文件下载完成后,前往您的 Flutter 应用目录,并将之前下载的google-services.json文件移动到android/app目录中。

  3. 文件移动后,在 Firebase 控制台中,点击下一步,如图所示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/8cdc637f-5749-4b21-b578-dda78daa6801.png

添加 Firebase SDK

现在我们已经下载并设置了配置文件,倒数第二步是将 Firebase SDK 添加到你的项目中。Gradle 的 Google 服务插件确保你下载的 JSON 文件被读取。为了在应用中启用 Google API 或 Firebase 服务,你必须添加一个 google-services 依赖项。需要对 build.gradle 文件进行两个小的修改以使用该插件。请看以下内容:

  1. 项目级别的 build.gradle (<project>/build.gradle):
buildscript { 
dependencies 
{ 
// Add this line 
classpath 'com.google.gms:google-services:4.2.0' 
} 
}
  1. 应用级别的 build.gradle (<project>/<app-module>/build.gradle):
dependencies {
  // Add this line
  *implementation 'com.google.firebase:firebase-core:16.0.1'* }
...
// Add to the bottom of the file
apply plugin: 'com.google.gms.google-services'
  1. 点击立即同步选项以完成此过程。

验证配置

完成前面的步骤后,我们必须验证你的 Flutter 应用是否连接到 Firebase。为了确保这一点,请按照以下步骤操作:

  1. 构建项目并在连接到你的计算机的设备上运行它。

  2. 一旦应用在手机上运行,Firebase 控制台将自动检测配置并显示如下成功消息:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/964ea405-ea49-4315-9399-35ac557b61bd.png

  1. 点击继续到控制台后,你将被带到显示项目名称和其他设置的控制台:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/d330d3de-d403-4b32-ab77-32c097d27638.png

在下一节中,我们将看到如何连接到云数据库。

创建 Cloud Firestore 数据库

一旦 Firebase-Flutter 设置完成,你就可以开始构建应用了。我们现在将设置 Cloud Firestore 数据库并初始化一些值。按照以下步骤操作:

  1. 在开发选项下,点击数据库。

  2. 在显示的面板中,点击创建数据库:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/82461193-593c-406a-bb05-fb2bf5622ff1.png

  1. 点击后,你会看到一个弹出面板:Cloud Firestore 的安全规则。选择以测试模式启动并启用:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/ead5da71-68cd-4755-9cfb-841c8a461650.png

  1. 我们选择测试模式,因为我们希望任何拥有数据库引用的人都能读取或写入数据库。当你构建应用的正式版本时,请确保你设置了安全规则。你可以在这里了解这些规则:firebase.google.com/docs/reference/rules/rules。点击启用后,Cloud Firestore 将配置安全规则并准备好使用。

  2. 在以下面板中,点击添加集合:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/6e7ad662-eb70-47ef-935d-4a59deaec5e3.png

  1. 我们假设在 Firestore 中我们只有一个集合,称为 Votes。集合是一组文档,构成了数据:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/0fd3ae41-6da4-4e00-b64b-3ba4e23acbec.png

  1. 点击下一步。

  2. 一个集合必须至少包含一个文档,这是 Cloud Firestore 的存储单元。你可以使用自动生成的 ID 或自定义 ID。在我们的例子中,我们使用 partyvotes。

  3. 对于现有的字段,输入名称的值(在我们的例子中,它是 VoteCount),选择数据类型,然后输入 partyvotes 的值。由于它是一个整数,我们选择数字并将其初始值设置为 0:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/7cb97e95-da8c-4ecc-87d4-37ab0a25e859.png

  1. 点击 保存。

  2. 在向你的集合添加了几个文档之后,你的数据库应该看起来像这样:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/29a54618-ee2e-4166-ab11-315b67d494c2.png

Firestore 是一个 NoSQL 数据库,这意味着我们不会与行和列一起工作。现在我们将构建应用的布局。使用 Firestore 的详细信息,我们将构建列表布局,这将根据 Firestore 中的值在运行时生成列表项,并在点击列表项时读取/更新 Firestore 数据库中的新值。以下是 main.dart 文件:

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

// Creating Object to temporary make the list items. We will replace it when we connect it to Firestore

final party = [
 {"partyname": "BJP", "rating": 1},
 {"partyname": "Congress", "rating": 3},
 {"partyname": "AAP", "rating": 5},
 {"partyname": "Janata Dal Party", "rating": 9},
 {"partyname": "NOTA", "rating": 11},
];

class MyApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'State Party Elections - Worker Profile',
     home: MyHomePage(),
   );
 }
}

class MyHomePage extends StatefulWidget {
 @override
 _MyHomePageState createState() {
   return _MyHomePageState();
 }
}

class _MyHomePageState extends State<MyHomePage> {
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(title: Text('Party Votes')),
     body: _buildBody(context),
   );
 }

 Widget _buildBody(BuildContext context) {
   // We will add the code here in the next section
   return _buildList(context, party);
 }

 Widget _buildList(BuildContext context, List<Map> snapshot) {
   return ListView(
     padding: const EdgeInsets.only(top: 22.0),
     children: snapshot.map((data) => _buildListItem(context, data)).toList(),
   );
 }

 Widget _buildListItem(BuildContext context, Map data) {
   final result = Record.fromMap(data);

// Adding the padding to ensure enough space is given
   return Padding(
     key: ValueKey(result.name),
     padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 7.0),
     child: Container(
       decoration: BoxDecoration(
         border: Border.all(color: Colors.red),
         borderRadius: BorderRadius.circular(6.0),
       ),
// Showing the list item, with name towards the left and the votes to the right

       child: ListTile(
         title: Text(record.partyname),
         trailing: Text(record.partyvotes.toString()),
         onTap: () => print(record),
       ),
     ),
   );
 }
}

class Record {
 final String partyname;
 final int partyvotes;
 final DocumentReference reference;

 Record.fromMap(Map<String, dynamic> map, {this.reference})
     : assert(map['partyname'] != null),
       assert(map['partyvotes'] != null),
       name = map['partyname'],
       votes = map['partyvotes'];

 Record.fromSnapshot(DocumentSnapshot snapshot)
     : this.fromMap(snapshot.data, reference: snapshot.reference);

 @override
 String toString() => "Record<$partyname:$partyvotes>";
}

我们在 Firestore 云上准备好了集合。在前面的例子中,我们使用了 party 对象。现在是时候使用 Firestore 云数据从我们的集合中显示了。我们可以通过调用 Cloud Firestore 使用一个 Firestore.instance 引用来做到这一点。例如,如果你希望从你的 Firestore 云数据库中调用一个特定的集合,你可以使用以下命令来返回一个快照流:

Firestore.instance.collection('collection_name').snapshots()

流有两种类型:单订阅或广播。流负责提供异步数据序列。用户生成的事件和从文件中读取的数据是两种数据序列。现在,使用 StreamBuilder 小部件,我们将数据流注入我们创建的用户界面。StreamBuilder 的一个经典用例是,每当 Firestore 值发生变化时,列表会自动更新。

在前面的代码中查找 _buildBody 方法,并将内容替换为以下代码:

Widget _buildBody(BuildContext context) {
 return StreamBuilder<QuerySnapshot>(
   stream: Firestore.instance.collection('party').snapshots(),
   builder: (context, snapshot) {
     if (!snapshot.hasData) return LinearProgressIndicator();
     return _buildList(context, snapshot.data.documents);
   },
 );
}

添加前面的片段会产生一些错误。_buildListItem 方法仍然认为它正在获取一个映射。因此,我们需要做一些修改。

首先,将方法修改为接受 DocumentSnapshot 而不是映射列表:

Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot)
{ .... 
}

其次,使用构造函数 Record.fromSnapshot() 来构建记录。该方法更新的代码如下:

Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
 final result = Record.fromSnapshot(data);

接下来,使用 onTap: () 方法确保每次点击列表项时,投票都会更新到 Firestore 数据库中。每次点击列表项时,Cloud Firestore 都会通知所有监听器更新后的快照。应用通过 StreamBuilder 激活参与,该小部件用于更新新数据。对于单个用户来说,这是可以的,但是当有多个用户时,可能会发生 竞态条件

main.dart 的完整代码如下:

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'State Party Elections - Worker Profile',
     home: MyHomePage(),
   );
 }
}

class MyHomePage extends StatefulWidget {
 @override
 _MyHomePageState createState() {
   return _MyHomePageState();
 }
}

class _MyHomePageState extends State<MyHomePage> {
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(title: Text('Party Votes')),
     body: _buildBody(context),
   );
 }

 Widget _buildBody(BuildContext context) {
   return StreamBuilder<QuerySnapshot>(
     stream: Firestore.instance.collection('party').snapshots(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) return LinearProgressIndicator();

       return _buildList(context, snapshot.data.documents);
     },
   );
 }

 Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
   return ListView(
     padding: const EdgeInsets.only(top: 22.0),
     children: snapshot.map((data) => _buildListItem(context, data)).toList(),
   );
 }

 Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
   final result = Record.fromSnapshot(data);

   return Padding(
     key: ValueKey(result.name),
     padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 
     7.0),
     child: Container(
       decoration: BoxDecoration(
         border: Border.all(color: Colors.red),
         borderRadius: BorderRadius.circular(6.0),
       ),
       child: ListTile(
         title: Text(record.name),
         trailing: Text(record.votes.toString()),
         onTap: () => Firestore.instance.runTransaction((transaction) 
         async {
           final freshFBsnapshot = await 
             transaction.get(record.reference);
            final updated = Record.fromSnapshot(freshFBsnapshot);

               await transaction
                   .update(record.reference, {'partyvotes': 
            updated.votes + 1});
             }),
       ),
     ),
   );
 }
}

class Record {
 final String partyname;
 final int partyvotes;
 final DocumentReference reference;

 Record.fromMap(Map<String, dynamic> map, {this.reference})
     : assert(map['partyname'] != null),
       assert(map['partyvotes'] != null),
       name = map['partyname'],
       votes = map['partyvotes'];

 Record.fromSnapshot(DocumentSnapshot snapshot)
     : this.fromMap(snapshot.data, reference: snapshot.reference);

 @override
 String toString() => "Record<$partyname:$partyvotes>";
}

一旦运行代码,尝试点击列表项,你将看到更新后的值映射在 Firestore 云数据库上。你也可以尝试在 Firestore 云数据库中更新列表项名称(在我们的例子中,是派对名称),你将看到列表项选项正在更新。

Firebase Cloud Messaging

Firebase Cloud Messaging (FCM) 是一种通过应用通知来提高应用内参与度的有效方式。使用 FCM,你可以向客户端设备发送两种类型的消息:

  1. 由 FCM SDK 直接处理的推送通知消息

  2. 数据消息

这两条消息的最大有效载荷为 4KB。当从 Firebase 控制台发送消息时,字符限制为 1,024 个字符。Firebase 拥有云消息传递以及应用内消息传递,但在这个部分,我们将仅讨论 Firebase 云消息传递。

在 Firebase 控制台中,点击左侧面板中的增长 | 云消息传递。随后点击发送第一条消息,如图所示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/564de0dc-d7cb-4179-9abb-037698ad19ea.png

要在您的设备上测试消息,需要 FCM 令牌。使用以下 Android 代码生成这些令牌:

FirebaseInstanceId.getInstance().getInstanceId()
        .addOnCompleteListener(new OnCompleteListener<InstanceIdResult>
         () {
            @Override
            public void onComplete(@NonNull Task<InstanceIdResult> 
            actionable) {
                if (!actionable.isSuccessful()) {
                    Log.w(TAG, "getInstanceId failed", 
                    actionable.getException());
                    return;
                }

                // Get new Instance ID token
                String tokenID = actionable.getResult().getToken();

                // Log and toast
                String message = getString(R.string.msg_token_fmt, 
                tokenID);
                Log.d(TAG, message);
                Toast.makeText(MainActivity.this, message, 
               Toast.LENGTH_SHORT).show();
            }
        });

FCM 还允许针对特定目标配置消息,例如GeoLocations、应用的版本、语言和用户受众。当您希望向特定用户组发送通知时,这种情况非常理想。

Firebase 远程配置

使用 Firebase 的远程配置 API,您可以在不实际下载应用更新的情况下更改应用。一个例子是在生产环境中推送新的应用更新时,当用户启动应用时,您可以向用户显示有关更新的弹出消息。

要设置 Firebase 远程配置,请转到 Firebase 控制台中的“增长”标签页,并点击“远程配置”,如图所示:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/1e9edec7-62aa-44a4-a151-fc3015d5d364.png

添加参数键和默认值。有一个可选字段用于添加描述。点击“添加参数”继续。请勿在远程配置中存储任何机密信息。Firebase 还允许为参数设置条件。例如,如果您想向印度的用户显示特定的欢迎消息,而向美国的用户显示不同的消息,远程配置将非常有用。

免责声明:一些代码文件是在Apache 2.0 许可证下授权的,并且可在firebase.google.com/docs/cloud-messaging/android/client上找到。

摘要

我们在本章开始时探讨了如何使用 Firestore 云数据库——NoSQL 方式帮助应用开发者实时构建应用,从而加快应用构建速度。我们还查看了一个使用 Firestore 云数据库捕获ListView的示例。本节之后,我们探讨了云消息传递的工作原理。在最后一节中,我们讨论了在您的应用中使用 Firebase 远程配置的一些用例。

在下一章中,我们将探讨如何部署您的 Flutter 应用程序。

第八章:部署 Flutter 应用

部署 Flutter 应用是使开发者能够为应用商店发布准备应用程序的最简单过程之一。到目前为止,您必须已经了解到 Google 的移动应用 SDK 提供了在 iOS 和 Android 上创建高质量原生界面的几个功能,且速度非常快。

在本章中,我们将探讨以下主题:

  • 在 Android 上部署

  • 在 iOS 上部署

在 Android 上部署

在本书到目前为止的内容中,我们构建的是调试类型。理想情况下,这用于在生成要上传的应用程序的发布版本之前测试应用程序。Flutter 还允许创建应用程序的变体。如果您想构建您开发的应用程序的生产版本,请遵循下面的步骤。

检查 AndroidManifest.xml 文件

此文件包含了一些在构建应用程序的生产版本时非常有用的主要全局设置。它位于 <app dir>/android/app/src/main。当您点击 AndroidManifest.xml 文件时,您将在 Application 标签中找到以下片段:

..
android:name="io.flutter.app.FlutterApplication"
android:label="flutter_app_battery"
android:icon="@mipmap/ic_launcher">.

以下代码中可见的属性的说明如下:

  • android.name: 这个属性设置了应用程序的包名

  • android.label: 这个属性设置了应用程序的最终名称

  • android:icon: 这个属性设置了应用程序的启动图标

.
<uses-permission android:name="android.permission.INTERNET"/>..

<uses-permission> 标签允许开发者设置应用程序中开发者所需的权限。例如,如果您想使用互联网,必须使用前面的属性,或者如果您想访问相机,必须使用 <uses-permission android:name="android.permission.CAMERA"/>。这将影响所有相机功能。开发者还可以要求在 运行时 模式下请求用户权限。

build.gradle 配置

下一步是检查位于 <app dir>/android/app 的 Gradle 构建文件,并确认以下参数中输入的值是否正确:

  • 在以下片段中设置 VersionCodeVersionName。请注意,VersionCode 的值对于每个上传的构建必须是唯一的,并且它是一个绝对值。Google Play 商店允许的 versionCode 的最大值是 2100000000。另一方面,VersionName 是一个字符串值。VersionName 没有在 Play 商店上显示值的用途。字符串值可以作为 <major>.<minor>.<point> 字符串级联——例如,1.2.2:
…….
…….
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null)
{ flutterVersionCode = '1' }
 def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null)
{ flutterVersionName = '1.0' }
……
……
  • applicationId: 这允许开发者指定最终的、唯一的应用程序 ID。

  • minSdkVersiontargetSdkVersion: 这两个值指定了应用程序设计运行的最低 API 级别和目标 API 级别:

defaultConfig
{ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "deviceinformation.flutterappbattery"
minSdkVersion 16
targetSdkVersion 27
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

应用程序内的图标

一个时尚、引人注目的图标可以成为某人启动应用的绝佳触发器。默认情况下,启动器图标是一个默认图标。遵循 Android Launcher Icon 指南,你可以构建自己的图标,该图标可用于从移动屏幕启动应用:

  1. 一旦你的图标文件准备就绪,检查 <app dir>/android/app/src/main/res/ 目录并将文件放入相应的文件夹中,使用配置限定符。你可以在此处了解更多信息:developer.android.com/guide/topics/resources/providing-resources#AlternativeResources

  2. 将文件放置在文件夹中后,只需转到 AndroidManifest.xml 并更新应用程序标签的 android:icon 属性。

  3. 为了确保图标已替换,使用 Flutter run 并检查启动器中的应用图标。

签名应用

这是发布应用到 Google Play 商店之前的关键步骤之一。要发布应用,使用数字签名对应用进行签名是关键部分。按照以下步骤对应用进行签名:

  1. 创建密钥库:如果你已经有了 keystore,则跳到步骤 2。如果你希望构建新的 keystore,请使用 KeyTool 工具使用以下命令行代码生成一个:
keytool -genkey -v -keystore ~/appkey.jks -keyalg RSA -keysize 2048 -validity 10000 -alias appkey

KeyTool 是 Java JDK 的一部分,它是 Android Studio 安装的一部分。确保在运行命令行之前提供一个绝对路径。此外,请注意生成的文件必须保持私密。

  1. 从应用中引用密钥库:接下来,创建一个名为

    <app dir>/android/key.properties 包含对 keystore 的引用。请保持此文件私密。查看以下代码:

storePassword=<password used in the previous step>
keyPassword=<password used in the previous step>
keyAlias=appkey
storeFile=<location of the key store file, e.g. /Users/<user name>/appkey.jks>
  1. 在 Gradle 中配置签名:转到 <app dir>/android/app/build.gradle 文件并将 **android {** 替换为以下代码:
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
  1. 接下来,删除以下代码:
buildTypes {
    release {
        // TODO: Add your own signing config for the release build.
        // Signing with the debug keys for now, so `flutter run --     
        //release` works.
        signingConfig signingConfigs.debug
    }
}

用以下代码替换它:

signingConfigs {
    release {
        keyAlias keystoreProperties['keyAlias']
        keyPassword keystoreProperties['keyPassword']
        storeFile file(keystoreProperties['storeFile'])
        storePassword keystoreProperties['storePassword']
    }
}
buildTypes {
    release {
        signingConfig signingConfigs.release
    }
}

完成这些步骤后,你的应用的发布构建将自动签名。

使用 ProGuard

默认情况下,Flutter 构建生成器不会对 Android Host 进行混淆或精简。你可能想减小 APK 的大小或防止代码被逆向工程。ProGuard 是保护你的代码的一种方式:

  1. 配置 ProGuard:创建一个名为 **/**android/app/proguard-rules.pro 的新文件并添加以下规则:
#Flutter Wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.**  { *; }
-keep class io.flutter.util.**  { *; }
-keep class io.flutter.view.**  { *; }
-keep class io.flutter.**  { *; }
-keep class io.flutter.plugins.**  { *; }

使用前面的代码,你可以仅保护 Flutter 中的引擎库。对于保护其他部分,根据你的开发需求添加代码。

  1. 启用混淆和/或精简:打开 /android/app/build.gradle 文件并定位到 buildTypes 定义:
....
buildTypes {
        release {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }....

在内部,将配置集的 minifyEnableduseProguard 标志设置为 true。注意也将 ProGuard 指向你在步骤 1 中创建的文件。刷新后的代码将如下所示:

…
…
buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled true
            useProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
…
…

构建发布 APK

在成功完成前面的步骤后,生成发布版本只是一个两步过程。使用命令行,执行以下操作:

  1. cd <app dir>(注意将<app dir>替换为您的应用程序目录路径)。

  2. 运行 flutter build apk。这将创建一个发布 APK,位于<app dir>/build/app/outputs/apk/release/app-release.apk

此构建可以在 Google Play 商店发布。确保在应用上传之前阅读发布指南。

在 iOS 上部署

就像 Google Play 商店一样,Apple 也有自己的应用发布指南。在构建应用之前,请务必阅读所有相关信息。以下是您可以查看以获取更多关于 Apple 应用发布的详细信息链接:developer.apple.com/app-store/review/。一旦应用提交,就像 Google 一样,Apple 将检查应用是否符合其发布指南。请注意,Flutter 支持 iOS 8.0 及更高版本。在设置 Xcode 构建生成时,这一点很重要。

就像 Google 一样,我们使用Google Play 开发者控制台。在 Apple 的情况下,我们将使用App Store Connect,之前被称为 iTunes。此控制台用于管理您应用的生命周期。此控制台将帮助您设置应用名称、描述和与应用一起发布的应用截图,并管理定价和发布。

注册 Bundle ID

在 Apple 商店发布的每个应用都有一个唯一的 Bundle ID,与 Apple 相关联。要为您的应用注册新的 Bundle ID,请按照以下步骤操作:

  1. 打开您的 Apple 开发者账户的 App IDs 页面。

  2. 点击+图标创建新的 Bundle ID。

  3. 输入应用名称并选择显式 App ID,然后输入一个 ID。

  4. 选择您的应用将要使用的服务,然后点击继续。

  5. 下一步确认详细信息。现在,点击注册以注册您的 Bundle ID。

在 App Store connect 上生成应用程序记录

要在 App Store connect 上注册应用,请按照以下步骤操作:

  1. 在浏览器中打开 Apple App Store connect 并点击我的应用

  2. 点击 My Apps 页面左上角的+图标 | 新建应用。

  3. 在弹出窗口中,填写您的应用详细信息。在“平台”部分,确保选中 iOS。此时,值得一提的是,Flutter 目前尚不支持 tvOS。因此,不要选中该复选框。应用名称不能超过 30 个字符。在 SKU 部分,添加一个在 App Store 中不可见的唯一 ID:

https://github.com/OpenDocCN/freelearn-mobi-zh/raw/master/docs/ggl-flt-mobidev-qkstgd/img/6dafc872-0735-46f9-9165-ddcd4bac208a.png

  1. 选择创建。

  2. 导航到使用之前步骤创建的应用程序详细信息,并在侧边栏中选择应用信息。

  3. 在“通用信息”部分选择 Bundle ID。

验证 Xcode 设置

在 Xcode 中验证构建发布设置相对简单,与 Android Studio 相比。首先,导航到 Xcode 中目标设置,并执行以下操作:

  1. 在 Xcode 中打开您的应用 ios 文件夹中的Runner.xcworkspace

  2. 从 Xcode 项目导航器中选择 Runner 项目,这会显示应用的设置。从主视图的侧边栏中选择 Runner 目标。

  3. 选择“通用”选项卡。

显示的信息需要你注意交叉检查重要的设置;因此,在“身份”部分,查看以下详细信息:

  • 显示名称:这是将在 App Store 以及任何其他使用名称的地方显示的应用名称

  • 包标识符:这是你在 App Store Connect 上注册的应用 ID,如前所述

在“签名应用”部分,请查看以下详细信息:

  • 自动管理签名:定义 Xcode 是否应该自动管理应用签名和配置。默认设置为True

  • 团队:选择与你的注册 Apple 开发者账户关联的团队。如果你希望添加更多成员,请点击添加账户,然后更新设置。

最后,在部署部分,检查部署目标:这包含你的应用将支持的最低 iOS 版本值。

选择应用图标

就像在 Android Studio 中一样,即使在 iOS 的情况下,也会创建一个占位符图标。如果你希望使用自己的图标,请在进行以下步骤之前阅读 iOS 应用图标指南:

  1. Runner文件夹中选择Assets.xcassets;这将在 Xcode 项目导航器中显示

  2. 如果你的图标已经准备好,请更新占位符图标为你的自定义应用图标

  3. 要检查图标是否已更新,请使用 Flutter Run 运行你的应用

创建构建存档

这是创建构建存档并将其上传到 Apple Store 的最后一步。在命令行中,在你的应用目录中按照以下步骤操作:

  1. 运行 flutter build iOS 以创建发布版本。

  2. 仅当你的 Xcode 版本低于 8.3 时才执行此操作。为了确保 Xcode 刷新发布模式配置,请重新启动你的 Xcode 工作区。

在 Xcode 中,请按照以下步骤配置应用版本和构建:

  1. 在 Xcode 中,在你的应用的ios文件夹中打开Runner.xcworkspace

  2. 选择产品 | 方案 | Runner。

  3. 选择产品 | 目标 | 通用 iOS 设备。

  4. 在 Xcode 项目导航器中选择 Runner,然后在设置视图侧边栏中选择 Runner 目标。

  5. 在“身份”部分,更新版本号,并将构建标识符更新为唯一的构建编号。这用于跟踪上传的构建数量。每个构建都应该有一个唯一的构建编号。

最后一步是创建构建存档并将其上传到 App Store Connect:

  1. 选择产品,然后生成构建存档

  2. 在 Xcode 组织者窗口的侧边栏中 | 选择 iOS 应用 | 选择你刚刚生成的构建存档

  3. 点击验证按钮

  4. 存档验证后,你可以点击“上传到 App Store”选项

如果有任何错误,请重新生成构建并尝试再次重复此过程。

摘要

一旦你完成了你的出色应用,部署和发布是关键环节。我们已经介绍了如何在 Play Store 上发布安卓和 iOS 应用。重要的是要知道,应用上传只是列出应用。你还应该将 App Store 视为提高应用可见性的关键技术。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值