Flutter - 快速指南


颤振 - 简介

一般来说,开发移动应用程序是一项复杂且具有挑战性的任务。有许多框架可用于开发移动应用程序。Android提供了基于Java语言的原生框架,iOS提供了基于Objective-C/Swift语言的原生框架。

但是,要开发支持这两种操作系统的应用程序,我们需要使用两种不同的框架以两种不同的语言进行编码。为了帮助克服这种复杂性,存在支持这两种操作系统的移动框架。这些框架的范围从简单的基于 HTML 的混合移动应用程序框架(使用 HTML 作为用户界面,使用 JavaScript 作为应用程序逻辑)到复杂的语言特定框架(将代码转换为本机代码的繁重工作)。无论其简单性或复杂性如何,这些框架总是有许多缺点,其中主要缺点之一是性能缓慢。

在这种场景下,Flutter——一个基于Dart语言的简单高性能框架,通过直接在操作系统的画布中渲染UI而不是通过原生框架来提供高性能。

Flutter 还提供了许多现成的小部件 (UI) 来创建现代应用程序。这些小部件针对移动环境进行了优化,使用小部件设计应用程序就像设计 HTML 一样简单。

具体来说,Flutter 应用程序本身就是一个 widget。Flutter 小部件还支持动画和手势。应用程序逻辑基于反应式编程。小部件可以选择具有状态。通过更改小部件的状态,Flutter 将自动(反应式编程)比较小部件的状态(旧的和新的),并仅使用必要的更改来渲染小部件,而不是重新渲染整个小部件。

我们将在接下来的章节中讨论完整的架构。

颤振的特点

Flutter 框架为开发人员提供以下功能 -

  • 现代且反应式的框架。

  • 使用Dart编程语言,非常简单易学。

  • 发展快。

  • 美观且流畅的用户界面。

  • 巨大的小部件目录。

  • 为多个平台运行相同的 UI。

  • 高性能应用程序。

颤振的优点

Flutter 配备了漂亮且可定制的小部件,可实现高性能和出色的移动应用程序。它满足所有定制需求和要求。除此之外,Flutter 还提供了更多优点,如下所述 -

  • Dart 拥有大型软件包存储库,可让您扩展应用程序的功能。

  • 开发人员只需为两个应用程序(Android 和 iOS 平台)编写一个代码库。Flutter将来也可能扩展到其他平台。

  • Flutter 需要较少的测试。由于其单一代码库,如果我们为这两个平台编写一次自动化测试就足够了。

  • Flutter 的简单性使其成为快速开发的良好选择。其定制能力和可扩展性使其如虎添翼。

  • 使用 Flutter,开发人员可以完全控制小部件及其布局。

  • Flutter 提供了出色的开发人员工具,以及令人惊叹的热重载。

颤振的缺点

尽管有很多优点,但 flutter 也有以下缺点 -

  • 由于它是用 Dart 语言编写的,因此开发人员需要学习新的语言(尽管它很容易学习)。

  • 现代框架试图尽可能地将逻辑和 UI 分开,但在 Flutter 中,用户界面和逻辑是混合在一起的。我们可以使用智能编码并使用高级模块来分离用户界面和逻辑来克服这个问题。

  • Flutter 是另一个创建移动应用程序的框架。开发人员很难在人口众多的细分市场中选择合适的开发工具。

颤振 - 安装

本章将详细指导您在本地计算机上安装 Flutter。

在 Windows 中安装

在本节中,让我们看看如何在 Windows 系统中安装Flutter SDK及其要求。

步骤 1 - 转到 URL https://flutter.dev/docs/get-started/install/windows并下载最新的 Flutter SDK。截至2019年4月,版本为1.2.1,文件为flutter_windows_v1.2.1-stable.zip。

步骤 2 - 将 zip 存档解压到文件夹中,例如 C:\flutter\

步骤 3 - 更新系统路径以包含 flutter bin 目录。

步骤 4 - Flutter 提供了一个工具 flutter doctor 来检查是否满足 flutter 开发的所有要求。

flutter doctor

步骤 5 - 运行上述命令将分析系统并显示其报告,如下所示 -

Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, v1.2.1, on Microsoft Windows [Version
10.0.17134.706], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK version
28.0.3)
[√] Android Studio (version 3.2)
[√] VS Code, 64-bit edition (version 1.29.1)
[!] Connected device
! No devices available
! Doctor found issues in 1 category.

报告称,所有开发工具均可用,但设备未连接。我们可以通过 USB 连接 Android 设备或启动 Android 模拟器来解决此问题。

步骤 6 - 安装最新的 Android SDK(如果 flutter doctor 报告)

步骤 7 - 安装最新的 Android Studio(如果 flutter doctor 报告)

步骤 8 - 启动 Android 模拟器或将真实的 Android 设备连接到系统。

步骤 9 - 安装适用于 Android Studio 的 Flutter 和 Dart 插件。它提供了创建新 Flutter 应用程序的启动模板、在 Android studio 本身中运行和调试 Flutter 应用程序的选项等,

  • 打开 Android Studio。

  • 单击文件 → 设置 → 插件。

  • 选择 Flutter 插件并单击“安装”。

  • 当提示安装 Dart 插件时,单击“是”。

  • 重新启动 Android Studio。

在 MacOS 中安装

要在 MacOS 上安装 Flutter,您必须按照以下步骤操作 -

步骤 1 - 转到 URL https://flutter.dev/docs/get-started/install/macos并下载最新的 Flutter SDK。截至 2019 年 4 月,版本为 1.2.1,文件为 flutter_macos_v1.2.1- stable.zip。

步骤 2 - 将 zip 存档解压到文件夹中,例如 /path/to/flutter

步骤 3 - 更新系统路径以包含 flutter bin 目录(在 ~/.bashrc 文件中)。

> export PATH = "$PATH:/path/to/flutter/bin"

步骤 4 - 使用以下命令在当前会话中启用更新的路径,然后也进行验证。

source ~/.bashrc
source $HOME/.bash_profile
echo $PATH

Flutter提供了一个工具flutter doctor来检查是否满足flutter开发的所有要求。它与 Windows 的对应部分类似。

步骤 5 - 安装最新的 XCode(如果 flutter doctor 报告)

步骤 6 - 安装最新的 Android SDK(如果 flutter doctor 报告)

步骤 7 - 安装最新的 Android Studio(如果 flutter doctor 报告)

步骤 8 - 启动 Android 模拟器或将真实的 Android 设备连接到系统来开发 Android 应用程序。

步骤 9 - 打开 iOS 模拟器或将真实的 iPhone 设备连接到系统来开发 iOS 应用程序。

步骤 10 - 安装适用于 Android Studio 的 Flutter 和 Dart 插件。它提供了创建新 Flutter 应用程序的启动模板、在 Android studio 本身中运行和调试 Flutter 应用程序的选项等,

  • 打开Android Studio

  • 单击首选项 → 插件

  • 选择Flutter插件并点击安装

  • 当提示安装 Dart 插件时,单击“是”。

  • 重新启动 Android Studio。

在 Android Studio 中创建简单的应用程序

在本章中,让我们创建一个简单的Flutter应用程序,以了解在 Android Studio 中创建 Flutter 应用程序的基础知识。

第 1 步- 打开 Android Studio

步骤 2 - 创建 Flutter 项目。为此,单击文件 → 新建 → 新建 Flutter 项目

新的颤振项目

步骤 3 - 选择 Flutter 应用程序。为此,选择Flutter Application并单击Next

颤振应用下一步

步骤 4 - 按如下方式配置应用程序,然后单击“下一步”

  • 项目名称:hello_app

  • Flutter SDK 路径:<path_to_flutter_sdk>

  • 项目位置:<项目文件夹路径>

  • 描述:基于 Flutter 的 hello world 应用程序

项目名

步骤 5 - 配置项目。

将公司域名设置为flutterapp.tutorialspoint.com并单击Finish

步骤 6 - 输入公司域名。

Android Studio 创建了一个功能齐全、功能最少的 flutter 应用程序。让我们检查应用程序的结构,然后更改代码来完成我们的任务。

应用程序的结构及其目的如下 -

结构应用

这里解释了应用程序结构的各个组件 -

  • android - 自动生成源代码来创建android应用程序

  • ios - 自动生成源代码来创建 ios 应用程序

  • lib - 包含使用 flutter 框架编写的 Dart 代码的主文件夹

  • ib/main.dart - Flutter 应用程序的入口点

  • test - 包含用于测试 flutter 应用程序的 Dart 代码的文件夹

  • test/widget_test.dart - 示例代码

  • .gitignore - Git 版本控制文件

  • .metadata - 由 flutter 工具自动生成

  • .packages - 自动生成以跟踪颤振包

  • .iml - Android studio 使用的项目文件

  • pubspec.yaml - 由Pub和 Flutter 包管理器使用

  • pubspec.lock - 由 Flutter 包管理器Pub自动生成

  • README.md - 以 Markdown 格式编写的项目描述文件

步骤 7 - 将lib/main.dart 文件中的 dart 代码替换为以下代码 -

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
   // This widget is the root of your application.
   @override
   Widget build(BuildContext context) {
      return MaterialApp(
         title: 'Hello World Demo Application',
         theme: ThemeData(
            primarySwatch: Colors.blue,
         ),
         home: MyHomePage(title: 'Home page'),
      );
   }
}
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key);
   final String title;

   @override
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(
            title: Text(this.title),
         ),
         body: Center(
            child:
            Text(
               'Hello World',
            )
         ),
      );
   }
}

让我们逐行理解 dart 代码。

  • 第 1 行- 导入 flutter 包,材质。该材质是一个 flutter 包,用于根据 Android 指定的材质设计指南创建用户界面。

  • 第 3 行- 这是 Flutter 应用程序的入口点。调用runApp函数并向其传递MyApp类的对象。runApp函数的目的是将给定的小部件附加到屏幕上。

  • 第 5-17 行- Widget 用于在 flutter 框架中创建 UI。StatelessWidget是一个widget,它不维护widget的任何状态。MyApp扩展了StatelessWidget并重写了它的构建方法build方法的目的 是创建应用程序 UI 的一部分。在这里,构建方法使用MaterialApp这个小部件来创建应用程序的根级 UI。它具有三个属性 - title 、 themehome

    • title是应用程序的标题

    • theme是小部件的主题。在这里,我们使用ThemeData类及其属性PrimarySwatch将蓝色设置为应用程序的整体颜色。

    • home 是应用程序的内部 UI,我们设置了另一个小部件MyHomePage

  • 第 19 - 38 行- MyHomePage与MyApp相同,只是它返回Scaffold Widget。Scaffold是紧邻MaterialApp小部件的顶级小部件,用于创建符合 UI 的材料设计。它有两个重要的属性,appBar用于显示应用程序的标题,body 用于显示应用程序的实际内容。AppBar是另一个用于呈现应用程序标题的小部件,我们在appBar属性中使用了它。在body属性中,我们使用了Center小部件,它将子小部件居中。文本是显示文本的最后一个、最里面的小部件,它显示在屏幕的中央。

步骤 8 - 现在,使用Run → Run main.dart运行应用程序

主镖

步骤 9 - 最后,应用程序的输出如下 -

主页

Flutter - 架构应用

本章我们来讨论Flutter框架的架构。

小部件

Flutter框架的核心理念是In Flutter, Everything is a widget。小部件基本上是用于创建应用程序用户界面的用户界面组件。

Flutter中,应用程序本身就是一个 widget。该应用程序是顶级小部件,其 UI 是使用一个或多个子小部件(小部件)构建的,而这些子小部件又使用其子小部件构建。这种可组合性功能可以帮助我们创建任何复杂的用户界面。

例如,hello world 应用程序(在上一章中创建)的小部件层次结构如下图所示 -

你好世界应用程序

这里值得注意的是以下几点 -

  • MyApp是用户创建的小部件,它是使用 Flutter 原生小部件MaterialApp构建的。

  • MaterialApp有一个 home 属性来指定主页的用户界面,这又是一个用户创建的小部件MyHomePage

  • MyHomePage是使用另一个 flutter 原生小部件Scaffold构建的

  • Scaffold有两个属性——bodyappBar

  • body用于指定其主用户界面,appBar用于指定其 header 用户界面

  • 标题 UI是使用 flutter 原生小部件构建的,AppBar正文 UI是使用中心小部件构建的。

  • Center小部件有一个属性Child,它引用实际内容,它是使用Text部件构建的

手势

Flutter 小部件支持通过特殊小部件GestureDetector进行交互。GestureDetector是一个不可见的小部件,能够捕获其子小部件的用户交互,例如点击、拖动等。Flutter 的许多原生小部件都支持通过使用GestureDetector进行交互。我们还可以通过将交互功能与GestureDetector小部件组合到现有小部件中。我们将在接下来的章节中单独学习手势。

国家概念

Flutter 小部件通过提供特殊的小部件StatefulWidget支持状态维护。Widget 需要从StatefulWidget派生来支持状态维护,所有其他 widget 都应该从StatefulWidget派生。Flutter 小部件在本机中是反应式的。这类似于reactjs,只要内部状态发生变化, StatefulWidget就会自动重新渲染。通过查找新旧小部件 UI 之间的差异并仅渲染必要的更改来优化重新渲染

层数

Flutter框架最重要的概念是,框架根据复杂性分为多个类别,并按照复杂性递减的层次清晰地排列。层是使用其紧邻的下一层来构建的。最顶层是AndroidiOS特有的小部件。下一层包含所有 flutter 原生小部件。下一层是渲染层,它是低级渲染器组件,渲染 flutter 应用程序中的所有内容。各层深入到核心平台特定代码

Flutter 中层的总体概述如下图所示 -

图层概述

以下几点总结了 Flutter 的架构 -

  • 在 Flutter 中,一切都是一个 widget,一个复杂的 widget 是由已经存在的 widget 组成的。

  • 必要时可以使用GestureDetector小部件合并交互功能。

  • 可以在必要时使用StatefulWidget小部件维护小部件的状态。

  • Flutter 提供分层设计,因此可以根据任务的复杂程度对任何层进行编程。

我们将在接下来的章节中详细讨论所有这些概念。

Flutter - Dart 编程简介

Dart 是一种开源通用编程语言。它最初是由谷歌开发的。Dart 是一种具有 C 风格语法的面向对象语言。它支持接口、类等编程概念,与其他编程语言不同,Dart 不支持数组。Dart 集合可用于复制数据结构,例如数组、泛型和可选类型。

以下代码显示了一个简单的 Dart 程序 -

void main() {
   print("Dart language is easy to learn");
}

变量和数据类型

变量被命名为存储位置,数据类型简单地指与变量和函数相关的数据的类型和大小。

Dart 使用var关键字来声明变量。var的语法定义如下,

var name = 'Dart';

Final和const关键字用于声明常量它们的定义如下 -

void main() {
   final a = 12;
   const pi = 3.14;
   print(a);
   print(pi);
}

Dart 语言支持以下数据类型 -

  • 数字- 它用于表示数字文字 - 整数和双精度。

  • 字符串- 它表示字符序列。字符串值用单引号或双引号指定。

  • 布尔值- Dart 使用bool关键字来表示布尔值 - true 和 false。

  • 列表和地图- 用于表示对象的集合。一个简单的列表可以定义如下:

void main() {
   var list = [1,2,3,4,5];
   print(list);
}

上面显示的列表生成 [1,2,3,4,5] 列表。

地图可以定义如下所示 -

void main() {
   var mapping = {'id': 1,'name':'Dart'};
   print(mapping);
}
  • 动态- 如果未定义变量类型,则其默认类型为动态。以下示例说明了动态类型变量 -

void main() {
   dynamic name = "Dart";
   print(name);
}

决策和循环

决策块在执行指令之前评估条件。Dart 支持 If、If..else 和 switch 语句。

循环用于重复一段代码,直到满足特定条件为止。Dart 支持 for、for..in、while 和 do..while 循环。

让我们了解一个关于控制语句和循环的使用的简单示例 -

void main() {
   for( var i = 1 ; i <= 10; i++ ) {
      if(i%2==0) {
         print(i);
      }
   }
}

上面的代码打印从 1 到 10 的偶数。

功能

函数是一组共同执行特定任务的语句。让我们看一下 Dart 中的一个简单函数,如下所示 -

void main() {
   add(3,4);
}
void add(int a,int b) {
   int c;
   c = a+b;
   print(c);
}

上述函数将两个值相加并产生 7 作为输出。

面向对象编程

Dart 是一种面向对象的语言。它支持面向对象的编程功能,如类、接口等。

类是创建对象的蓝图。类定义包括以下内容 -

  • 领域
  • 获取器和设置器
  • 构造函数
  • 功能

现在,让我们使用上面的定义创建一个简单的类 -

class Employee {
   String name;
   
   //getter method
   String get emp_name {
      return name;
   }
   //setter method
   void set emp_name(String name) {
      this.name = name;
   }
   //function definition
   void result() {
      print(name);
   }
}
void main() {
   //object creation
   Employee emp = new Employee();
   emp.name = "employee1";
   emp.result(); //function call
}

Flutter - Widget 简介

正如我们在前面的章节中了解到的,小部件就是 Flutter 框架中的一切。我们已经在前面的章节中学习了如何创建新的小部件。

在本章中,让我们了解创建小部件背后的实际概念以及Flutter框架中可用的不同类型的小部件。

让我们检查一下Hello World应用程序的MyHomePage小部件。用于此目的的代码如下所示 -

class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title}) : super(key: key); 
   
   final String title; 
   @override 
   Widget build(BuildContext context) {
      return Scaffold( 
         appBar: AppBar(title: Text(this.title), ), 
         body: Center(child: Text( 'Hello World',)),
      );
   }
}

在这里,我们通过扩展StatelessWidget创建了一个新的小部件。

请注意,StatelessWidget仅需要在其派生类中实现一个方法构建。build方法通过BuildContext参数获取构建widget所需的上下文环境,并返回它构建widget。

在代码中,我们使用title作为构造函数参数之一,并使用Key作为另一个参数。title用于显示标题,Key用于在构建环境中标识widget

这里,build方法调用Scaffold的build方法,Scaffold 又调用AppBarCenter的build方法来构建其用户界面。

最后,Center构建方法调用Text构建方法。

为了更好地理解,下面给出了相同的视觉表示 -

视觉表现

小部件构建可视化

Flutter中,小部件可以根据其功能分为多个类别,如下所示 -

  • 平台特定的小部件
  • 布局小部件
  • 状态维护小部件
  • 平台独立/基本小部件

现在让我们详细讨论每一个。

平台特定的小部件

Flutter 具有特定于特定平台(Android 或 iOS)的小部件。

Android 特定的小部件是根据Android 操作系统的材料设计指南设计的。Android 特定的小部件称为Material 小部件

iOS 特定的小部件是根据Apple 的人机界面指南设计的,它们被称为Cupertino小部件。

一些最常用的材质小部件如下 -

  • 脚手架
  • 应用栏
  • 底部导航栏
  • 标签栏
  • 标签栏视图
  • 列表图块
  • 凸起按钮
  • 浮动操作按钮
  • 平按钮
  • 图标按钮
  • 下拉按钮
  • 弹出菜单按钮
  • 按钮栏
  • 文本域
  • 复选框
  • 收音机
  • 转变
  • 滑块
  • 日期和时间选择器
  • 简单对话框
  • 警报对话框

一些最常用的库比蒂诺小部件如下 -

  • 库比蒂诺巴顿
  • 库比蒂诺挑选器
  • 库比蒂诺日期选择器
  • 库比蒂诺定时器选择器
  • 库比蒂诺导航栏
  • 库比蒂诺标签栏
  • 库比蒂诺Tab脚手架
  • 库比蒂诺选项卡视图
  • 库比蒂诺文本字段
  • 库比蒂诺对话
  • CupertinoDialog动作
  • 库比蒂诺全屏对话框过渡
  • 库比蒂诺页面脚手架
  • 库比蒂诺页面转换
  • 库比蒂诺行动表
  • 库比蒂诺活动指标
  • 库比蒂诺警报对话框
  • 库比蒂诺PopupSurface
  • 库比蒂诺滑块

布局小部件

在 Flutter 中,可以通过组合一个或多个 widget 来创建一个 widget。为了将多个 Widget 组合成一个 Widget,Flutter提供了大量具有布局功能的 Widget。例如,可以使用Center widget 将子 widget 居中。

一些流行的布局小部件如下 -

  • Container - 使用具有背景、边框和阴影的BoxDecoration小部件装饰的矩形框。

  • Center - 居中其子部件。

  • Row - 在水平方向排列其子项。

  • Column - 在垂直方向排列其子项。

  • Stack - 将一个排列在另一个之上。

我们将在即将到来的布局小部件简介章节中详细检查布局小部件。

状态维护小部件

在 Flutter 中,所有 widget 都派生自StatelessWidgetStatefulWidget

从StatelessWidget派生的 Widget没有任何状态信息,但它可能包含从StatefulWidget派生的 Widget 。应用程序的动态本质是通过小部件的交互Behave以及交互过程中状态的变化来实现的。例如,点击计数器按钮将使计数器的内部状态增加/减少 1,Flutter小部件的反应性质将使用新的状态信息自动重新渲染小部件。

我们将在接下来的状态管理章节中详细学习StatefulWidget小部件的概念。

平台独立/基本小部件

Flutter提供了大量的基本小部件,以独立于平台的方式创建简单和复杂的用户界面。让我们看看本章中的一些基本小部件。

文本

文本小部件用于显示一段字符串。字符串的样式可以通过使用style属性和TextStyle类来设置。用于此目的的示例代码如下 -

Text('Hello World!', style: TextStyle(fontWeight: FontWeight.bold))

文本小部件有一个特殊的构造函数Text.rich ,它接受TextSpan类型的子代来指定具有不同样式的字符串。TextSpan小部件本质上是递归的,它接受TextSpan作为其子项。用于此目的的示例代码如下 -

Text.rich( 
   TextSpan( 
      children: <TextSpan>[ 
         TextSpan(text: "Hello ", style:  
         TextStyle(fontStyle: FontStyle.italic)),  
         TextSpan(text: "World", style: 
         TextStyle(fontWeight: FontWeight.bold)),  
      ], 
   ), 
)

文本小部件最重要的属性如下 -

  • maxLines, int - 要显示的最大行数

  • Overflow, TextOverFlow - 指定如何使用TextOverFlow类处理视觉溢出

  • style, TextStyle - 使用TextStyle类指定字符串的样式

  • textAlign, TextAlign - 使用TextAlign类进行文本对齐,如右对齐、左对齐、对齐等

  • textDirection, TextDirection - 文本流动的方向,从左到右或从右到左

图像

图像小部件用于在应用程序中显示图像。图像小部件提供了不同的构造函数来从多个源加载图像,它们如下 -

  • Image - 使用ImageProvider 的通用图像加载器

  • Image.asset - 从 flutter 项目的资产加载图像

  • Image.file - 从系统文件夹加载图像

  • Image.memory - 从内存加载图像

  • Image.Network - 从网络加载图像

在Flutter中加载和显示图像的最简单选项是将图像作为应用程序的资产并根据需要将其加载到小部件中。

  • 在项目文件夹中创建一个文件夹,assets 并放置必要的图像。

  • 在 pubspec.yaml 中指定资产,如下所示 -

flutter: 
   assets: 
      - assets/smiley.png
  • 现在,在应用程序中加载并显示图像。

Image.asset('assets/smiley.png')
  • hello world 应用程序的MyHomePage小部件的完整源代码和结果如下所示:

class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 

   @override 
   Widget build(BuildContext context) {
      return Scaffold( 
         appBar: AppBar( title: Text(this.title), ), 
         body: Center( child: Image.asset("assets/smiley.png")),
      ); 
   }
}

加载的图像如下所示 -

Hello World 应用程序输出

图像小部件最重要的属性如下 -

  • image, ImageProvider - 要加载的实际图像

  • width, double - 图像的宽度

  • height, double - 图像的高度

  • alignment, AlignmentGeometry - 如何在其边界内对齐图像

图标

图标小部件用于显示IconData类中描述的字体的字形。加载简单电子邮件图标的代码如下 -

Icon(Icons.email)

在 hello world 应用程序中应用它的完整源代码如下 -

class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 

   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text(this.title),),
         body: Center( child: Icon(Icons.email)),
      );
   }
}

加载的图标如下所示 -

主页

Flutter - 布局简介

由于Flutter的核心概念是Everything is widgetFlutter将用户界面布局功能合并到 widget 本身中。Flutter提供了相当多专门设计的 widget,如Container、Center、Align等,只是为了布局用户界面。通过组合其他小部件构建的小部件通常使用布局小部件。让我们在本章中学习Flutter布局概念。

布局小部件的类型

布局小部件可以根据其子级分为两个不同的类别 -

  • 支持单个孩子的小部件
  • 支持多个孩子的小部件

让我们在接下来的部分中了解这两种类型的小部件及其功能。

独生子女小部件

在此类别中,小部件将只有一个小部件作为其子部件,并且每个小部件都将具有特殊的布局功能。

例如,中心小部件只是将其子小部件相对于其父小部件居中,而容器小部件提供了完全的灵活性,可以使用不同的选项(如填充、装饰等)将其子部件放置在其中的任何给定位置,

单子小部件是创建具有单一功能(如按钮、标签等)的高质量小部件的绝佳选择,

使用容器小部件创建简单按钮的代码如下 -

class MyButton extends StatelessWidget {
   MyButton({Key key}) : super(key: key); 

   @override 
   Widget build(BuildContext context) {
      return Container(
         decoration: const BoxDecoration(
            border: Border(
               top: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)),
               left: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)),
               right: BorderSide(width: 1.0, color: Color(0xFFFF000000)),
               bottom: BorderSide(width: 1.0, color: Color(0xFFFF000000)),
            ),
         ),
         child: Container(
            padding: const
            EdgeInsets.symmetric(horizontal: 20.0, vertical: 2.0),
            decoration: const BoxDecoration(
               border: Border(
                  top: BorderSide(width: 1.0, color: Color(0xFFFFDFDFDF)),
                  left: BorderSide(width: 1.0, color: Color(0xFFFFDFDFDF)),
                  right: BorderSide(width: 1.0, color: Color(0xFFFF7F7F7F)),
                  bottom: BorderSide(width: 1.0, color: Color(0xFFFF7F7F7F)),
               ),
               color: Colors.grey,
            ),
            child: const Text(
               'OK',textAlign: TextAlign.center, style: TextStyle(color: Colors.black)
            ), 
         ), 
      ); 
   }
}

在这里,我们使用了两个小部件 -容器小部件和文本小部件。小部件的结果是一个自定义按钮,如下所示 -

好的

让我们检查一下Flutter提供的一些最重要的单子布局小部件-

  • Padding - 用于按给定的填充来排列其子部件。这里,填充可以由EdgeInsets类提供。

  • 对齐- 使用对齐属性的值在其内部对齐其子窗口小部件。对齐属性的值可以由FractionalOffset类提供。FractionalOffset类指定以距左上角的距离为单位的偏移量

一些可能的偏移值如下 -

  • FractionalOffset(1.0, 0.0) 表示右上角。

  • FractionalOffset(0.0, 1.0) 表示左下角。

关于偏移量的示例代码如下所示 -

Center(
   child: Container(
      height: 100.0, 
      width: 100.0, 
      color: Colors.yellow, child: Align(
         alignment: FractionalOffset(0.2, 0.6),
         child: Container( height: 40.0, width:
            40.0, color: Colors.red,
         ), 
      ), 
   ), 
)
  • FittedBox - 它缩放子窗口小部件,然后根据指定的配合定位它。

  • AspectRatio - 它尝试将子窗口小部件的大小调整为指定的宽高比

  • 约束框

  • 基线

  • 分形大小的盒子

  • 固有高度

  • 固有宽度

  • 限量盒装

  • 舞台外

  • 溢出盒

  • 大小盒

  • 大小溢出框

  • 转换

  • 自定义单子布局

我们的 hello world 应用程序使用基于材质的布局小部件来设计主页。让我们修改我们的 hello world 应用程序以使用基本布局小部件构建主页,如下所示 -

  • Container - 通用、单子、基于框的容器小部件,具有对齐、填充、边框和边距以及丰富的样式功能。

  • Center - 简单的单个子容器小部件,将其子小部件居中。

MyHomePageMyApp小部件的修改代码如下 -

class MyApp extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
      return MyHomePage(title: "Hello World demo app");
   }
}
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key);
   final String title;
   @override
   Widget build(BuildContext context) {
      return Container(
         decoration: BoxDecoration(color: Colors.white,),
         padding: EdgeInsets.all(25), child: Center(
            child:Text(
               'Hello World', style: TextStyle(
                  color: Colors.black, letterSpacing: 0.5, fontSize: 20,
               ),
               textDirection: TextDirection.ltr,
            ),
         )
      );
   }
}

这里,

  • 容器小部件是顶层或根小部件。容器使用装饰填充属性来配置其内容。

  • BoxDecoration有许多属性,如颜色、边框等,用于装饰容器小部件,这里,颜色用于设置容器的颜色。

  • 容器小部件的填充是使用dgeInsets类设置的,该类提供了指定填充值的选项。

  • Center是Container小部件的子小部件。同样,Text是Center小部件的子项。Text用于显示消息,Center用于将文本消息相对于父窗口小部件Container居中。

上面给出的代码的最终结果是一个布局示例,如下所示 -

最后结果

多个子部件

在此类别中,给定的小部件将具有多个子小部件,并且每个小部件的布局都是唯一的。

例如,“行”小部件允许在水平方向上布置其子部件,而“列”小部件允许在垂直方向上布置其子部件。通过组合RowColumn,可以构建任何复杂程度的小部件。

让我们在本节中了解一些常用的小部件。

  • Row - 允许以水平方式排列其子项。

  • Column - 允许以垂直方式排列其子项。

  • ListView - 允许将其子项排列为列表。

  • GridView - 允许将其子项排列为画廊。

  • Expanded - 用于使行和列小部件的子项占据最大可能的区域。

  • - 基于表的小部件。

  • Flow - 基于流的小部件。

  • Stack - 基于堆栈的小部件。

高级布局应用

在本节中,让我们学习如何使用单个和多个子布局小部件通过自定义设计创建复杂的产品列表用户界面。

为此,请遵循以下顺序 -

  • 在 Android studio 中创建一个新的Flutter应用程序, product_layout_app

  • main.dart代码替换为以下代码 -

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class MyApp extends StatelessWidget {
   // This widget is the root of your application.
   @override 
   Widget build(BuildContext context) {
      return MaterialApp( 
         title: 'Flutter Demo', theme: ThemeData( 
         primarySwatch: Colors.blue,), 
         home: MyHomePage(title: 'Product layout demo home page'),
      ); 
   } 
} 
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 
      
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text(this.title),), 
         body: Center(child: Text( 'Hello World', )), 
      ); 
   }
}
  • 这里,

  • 我们通过扩展StatelessWidget而不是默认的StatefulWidget创建了MyHomePage小部件,然后删除了相关代码。

  • 现在,根据指定的设计创建一个新的小部件ProductBox ,如下所示 -

产品盒
  • ProductBox的代码如下。

class ProductBox extends StatelessWidget {
   ProductBox({Key key, this.name, this.description, this.price, this.image}) 
      : super(key: key); 
   final String name; 
   final String description; 
   final int price; 
   final String image; 

   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), height: 120,  child: Card( 
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[
                  Image.asset("assets/appimages/" +image), Expanded(
                     child: Container(
                        padding: EdgeInsets.all(5), child: Column(
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                              children: <Widget>[ 
                              
                              Text(this.name, style: TextStyle(fontWeight: 
                                 FontWeight.bold)), Text(this.description), 
                              Text("Price: " + this.price.toString()), 
                           ], 
                        )
                     )
                  )
               ]
            )
         )
      );
   }
}
  • 请遵守代码中的以下内容 -

  • ProductBox使用了四个参数,如下所示 -

    • 名称 - 产品名称

    • 描述 - 产品描述

    • 价格 - 产品的价格

    • 图片 - 产品图片

  • ProductBox使用七个内置小部件,如下所示 -

    • 容器
    • 扩展
    • 柱子
    • 卡片
    • 文本
    • 图像
  • ProductBox是使用上述小部件设计的。小部件的排列或层次结构如下图所示 -

小部件的层次结构
  • 现在,在应用程序的资产文件夹中放置一些用于产品信息的虚拟图像(见下文),并在 pubspec.yaml 文件中配置资产文件夹,如下所示 -

assets: 
   - assets/appimages/floppy.png 
   - assets/appimages/iphone.png 
   - assets/appimages/laptop.png 
   - assets/appimages/pendrive.png 
   - assets/appimages/pixel.png 
   - assets/appimages/tablet.png
iPhone

iPhone.png

像素

像素.png

笔记本电脑

笔记本电脑.png

药片

平板电脑.png

随身碟

Pendrive.png

软盘

软盘.png

最后,使用MyHomePage小部件中的ProductBox小部件,如下所示 -

class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 

   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title:Text("Product Listing")), 
         body: ListView(
            shrinkWrap: true, padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), 
            children: <Widget> [
               ProductBox(
                  name: "iPhone", 
                  description: "iPhone is the stylist phone ever", 
                  price: 1000, 
                  image: "iphone.png"
               ), 
               ProductBox(
                  name: "Pixel", 
                  description: "Pixel is the most featureful phone ever", 
                  price: 800, 
                  image: "pixel.png"
               ), 
               ProductBox( 
                  name: "Laptop", 
                  description: "Laptop is most productive development tool", 
                  price: 2000, 
                  image: "laptop.png"
               ), 
               ProductBox( 
                  name: "Tablet", 
                  description: "Tablet is the most useful device ever for meeting", 
                  price: 1500, 
                  image: "tablet.png"
               ), 
               ProductBox(
                  name: "Pendrive", 
                  description: "Pendrive is useful storage medium", 
                  price: 100, 
                  image: "pendrive.png"
               ), 
               ProductBox(
                  name: "Floppy Drive", 
                  description: "Floppy drive is useful rescue storage medium", 
                  price: 20, 
                  image: "floppy.png"
               ), 
            ],
         )
      );
   }
}
  • 在这里,我们使用ProductBox作为ListView小部件的子项。

  • 产品布局应用程序(product_layout_app)的完整代码(main.dart)如下 -

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class MyApp extends StatelessWidget { 
   // This widget is the root of your application. 
   @override 
   Widget build(BuildContext context) {
      return MaterialApp(
         title: 'Flutter Demo', theme: ThemeData(
            primarySwatch: Colors.blue,
         ), 
         home: MyHomePage(title: 'Product layout demo home page'), 
      );
   }
}
class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 
   
   @override 
   Widget build(BuildContext context) { 
      return Scaffold( 
         appBar: AppBar(title: Text("Product Listing")), 
         body: ListView(
            shrinkWrap: true, 
            padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), 
            children: <Widget>[ 
               ProductBox(
                  name: "iPhone", 
                  description: "iPhone is the stylist phone ever", 
                  price: 1000, 
                  image: "iphone.png"
               ), 
               ProductBox( 
                  name: "Pixel",    
                  description: "Pixel is the most featureful phone ever", 
                  price: 800, 
                  image: "pixel.png"
               ), 
               ProductBox( 
                  name: "Laptop", 
                  description: "Laptop is most productive development tool", 
                  price: 2000, 
                  image: "laptop.png"
               ), 
               ProductBox( 
                  name: "Tablet", 
                  description: "Tablet is the most useful device ever for meeting", 
                  price: 1500, 
                  image: "tablet.png"
               ), 
               ProductBox( 
                  name: "Pendrive", 
                  description: "Pendrive is useful storage medium", 
                  price: 100, 
                  image: "pendrive.png"
               ), 
               ProductBox(
                  name: "Floppy Drive", 
                  description: "Floppy drive is useful rescue storage medium", 
                  price: 20, 
                  image: "floppy.png"
               ), 
            ],
         )
      );
   }
}
class ProductBox extends StatelessWidget {
   ProductBox({Key key, this.name, this.description, this.price, this.image}) :
      super(key: key); 
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   
   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), 
         height: 120, 
         child: Card(
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
               children: <Widget>[ 
                  Image.asset("assets/appimages/" + image), 
                  Expanded( 
                     child: Container( 
                        padding: EdgeInsets.all(5), 
                        child: Column(    
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                           children: <Widget>[ 
                              Text(
                                 this.name, style: TextStyle(
                                    fontWeight: FontWeight.bold
                                 )
                              ),
                              Text(this.description), Text(
                                 "Price: " + this.price.toString()
                              ), 
                           ], 
                        )
                     )
                  )
               ]
            )
         )
      );
   }
}

应用程序的最终输出如下 -

产品列表

Flutter - 手势简介

手势主要是用户与移动(或任何基于触摸的设备)应用程序交互的一种方式。手势通常被定义为用户意图激活移动设备的特定控制的任何物理动作/移动。手势就像点击移动设备的屏幕一样简单,也可以在游戏应用程序中使用更复杂的操作。

这里提到了一些广泛使用的手势 -

  • 点击- 用指尖短时间触摸设备表面,然后松开指尖。

  • 双击- 短时间内点击两次。

  • 拖动- 用指尖触摸设备表面,然后稳定地移动指尖,最后松开指尖。

  • 轻拂- 与拖动类似,但速度更快。

  • - 用两根手指捏住设备的表面。

  • 展开/缩放- 与捏合相反。

  • 平移- 用指尖触摸设备表面并在不松开指尖的情况下向任意方向移动设备。

Flutter 通过其独有的小部件GestureDetector为所有类型的手势提供了出色的支持。GestureDetector 是一个非视觉小部件,主要用于检测用户的手势。要识别针对小部件的手势,可以将该小部件放置在 GestureDetector 小部件内。GestureDetector 将捕获手势并根据手势调度多个事件。

下面给出了一些手势和相应的事件 -

  • 轻敲
    • 按下按钮
    • 点击向上
    • 点按
    • 点击取消
  • 双击
    • 双击
  • 长按
    • 长按时
  • 垂直拖动
    • onVerticalDragStart
    • onVerticalDragUpdate
    • onVerticalDragEnd
  • 水平拖动
    • onHorizo​​ntalDragStart
    • onHorizo​​ntalDragUpdate
    • onHorizo​​ntalDragEnd
  • 平底锅
    • 平移启动
    • 泛更新
    • 平移结束

现在,让我们修改 hello world 应用程序以包含手势检测功能并尝试理解这个概念。

  • 更改MyHomePage小部件的正文内容,如下所示 -

body: Center( 
   child: GestureDetector( 
      onTap: () { 
         _showDialog(context); 
      }, 
      child: Text( 'Hello World', ) 
   ) 
),
  • 请注意,这里我们已将GestureDetector小部件放置在小部件层次结构中的“文本”小部件之上,捕获 onTap 事件,然后最终显示一个对话框窗口。

  • 实现 *_showDialog* 函数以在用户点击hello world 消息时显示一个对话框。它使用通用的showDialogAlertDialog小部件来创建新的对话框小部件。代码如下所示 -

// user defined function void _showDialog(BuildContext context) { 
   // flutter defined function 
   showDialog( 
      context: context, builder: (BuildContext context) { 
         // return object of type Dialog
         return AlertDialog( 
            title: new Text("Message"), 
            content: new Text("Hello World"),   
            actions: <Widget>[ 
               new FlatButton( 
                  child: new Text("Close"),  
                  onPressed: () {   
                     Navigator.of(context).pop();  
                  }, 
               ), 
            ], 
         ); 
      }, 
   ); 
}
  • 应用程序将使用热重载功能在设备中重新加载。现在,只需单击消息“Hello World”,它将显示如下对话框 -

热重载功能
  • 现在,通过单击对话框中的关闭选项来关闭对话框。

  • 完整代码(main.dart)如下 -

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class MyApp extends StatelessWidget { 
   // This widget is the root of your application.    
   @override 
   Widget build(BuildContext context) {
      return MaterialApp(
         title: 'Hello World Demo Application', 
         theme: ThemeData( primarySwatch: Colors.blue,), 
         home: MyHomePage(title: 'Home page'), 
      ); 
   }
}
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 
   
   // user defined function 
   void _showDialog(BuildContext context) { 
      // flutter defined function showDialog( 
         context: context, builder: (BuildContext context) { 
            // return object of type Dialog return AlertDialog(
               title: new Text("Message"), 
               content: new Text("Hello World"),   
               actions: <Widget>[
                  new FlatButton(
                     child: new Text("Close"), 
                     onPressed: () {   
                        Navigator.of(context).pop();  
                     }, 
                  ), 
               ],
            );
         },
      );
   }
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text(this.title),),
         body: Center(
            child: GestureDetector( 
               onTap: () {
                  _showDialog(context);
               },
            child: Text( 'Hello World', )
            )
         ),
      );
   }
}

最后,Flutter 还通过Listener小部件提供了低级手势检测机制。它将检测所有用户交互,然后调度以下事件 -

  • 指针向下事件
  • 指针移动事件
  • 指针向上事件
  • 指针取消事件

Flutter 还提供了一小组小部件来执行特定的高级手势。下面列出了小部件 -

  • 可关闭- 支持轻拂手势来关闭小部件。

  • Draggable - 支持拖动手势来移动小部件。

  • LongPressDraggable - 当其父窗口小部件也可拖动时,支持拖动手势来移动窗口小部件。

  • DragTarget - 接受任何可拖动的小部件

  • IgnorePointer - 在手势检测过程中隐藏小部件及其子部件。

  • AbsorbPointer - 停止手势检测过程本身,因此任何重叠的小部件也无法参与手势检测过程,因此不会引发任何事件。

  • 可滚动- 支持滚动小部件内可用的内容。

Flutter - 状态管理

管理应用程序中的状态是应用程序生命周期中最重要和必要的过程之一。

让我们考虑一个简单的购物车应用程序。

  • 用户将使用其凭据登录应用程序。

  • 用户登录后,应用程序应在所有屏幕中保留登录的用户详细信息。

  • 同样,当用户选择产品并将其保存到购物车中时,购物车信息应在页面之间保留,直到用户签出购物车为止。

  • 任何情况下的用户及其购物车信息都称为该情况下应用程序的状态。

根据特定状态在应用程序中持续的持续时间,状态管理可以分为两类。

  • 短暂的- 持续几秒钟,如动画的当前状态或单个页面,如产品的当前评级。Flutter通过 StatefulWidget 来支持它。

  • 应用程序状态 - 最后是整个应用程序,例如登录的用户详细信息、购物车信息等, Flutter通过scoped_model 支持它。

导航和路线选择

在任何应用程序中,从一个页面/屏幕导航到另一页面/屏幕定义了应用程序的工作流程。处理应用程序导航的方式称为路由。Flutter 提供了一个基本的路由类 - MaterialPageRoute 和两个方法 - Navigator.push 和 Navigator.pop,来定义应用程序的工作流程。

材质页面路由

MaterialPageRoute 是一个小部件,用于通过代表渲染其 UI