Flutter开发技术与分享(二) —— Flutter 入门(
版本记录
版本号 | 时间 |
---|---|
V1.0 | 2019.11.03 星期日 |
前言
Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。目前公司的部分模块就是在使用Flutter进行开发。感兴趣的可以看下面几篇文章。
1. Flutter开发技术与分享(一) —— 基本概览(一)
开始
首先看下主要内容
通过使用
VS Code
编写跨平台应用程序,深入研究Flutter
框架,以在单个代码库中构建iOS
和Android
应用程序。下面是翻译文章的地址。
然后看下写作环境
写作环境:Dart 2, Flutter 1.7, VS Code
自十年前iOS
和Android
平台风起云涌以来,跨平台开发一直是整个移动开发领域的目标。 能够为iOS和Android编写一个应用程序的功能可以为您的公司和团队节省大量时间和精力。
多年来,已经发布了用于跨平台开发的各种工具,包括基于Web的工具(例如Adobe
的PhoneGap),强大的框架(例如Microsoft
的Xamarin)以及更新的工具(例如Facebook
的React Native)。 每个工具集都有其优缺点,并且在移动行业中获得了不同程度的成功。
进入跨平台领域的最新框架是Google
的Flutter。 Flutter在两个平台上均具有快速的开发周期,快速的UI呈现,独特的UI设计以及本机应用程序性能。
Introduction to Flutter
Flutter
应用程序是使用Dart编程语言编写的,该语言最初也来自Google
,现在是ECMA
标准。 Dart
与其他现代语言(例如Kotlin
和Swift
)具有许多相同的功能,并且可以转编译为JavaScript
代码。
作为跨平台框架,Flutter
最类似于React Native
,因为Flutter
允许响应式和声明式编程风格。但是,与React Native
不同,Flutter
不需要使用Javascript
桥接,这可以缩短应用程序的启动时间和整体性能。 Dart
通过使用Ahead-Of-Time
或AOT
编译来实现此目的。
Dart
的另一个独特之处在于它还可以使用Just-In-Time or JIT
编译。 Flutter
的JIT
编译通过允许热重装(hot reload)
功能在开发过程中刷新UI而无需全新的构建,从而改善了开发工作流程。
正如您将在本教程中看到的那样,Flutter
框架主要围绕widgets
的概念构建。在Flutter中,widgets
不仅用于应用程序的视图,而且还用于整个屏幕甚至应用程序本身。
除了跨平台的iOS和Android开发之外,学习Flutter还可以让您抢先开发Fuchsia平台,Fuchsia
平台目前是Google
开发的实验性操作系统。
在本教程中,您将构建一个Flutter
应用程序,该应用程序将查询GitHub API中的GitHub组织中的团队成员,并在可滚动列表中显示团队成员信息:
您可以同时使用iOS
模拟器或Android
模拟器来开发应用程序!
在构建应用程序时,您将了解有关Flutter的以下知识:
Setting up your development environment
Creating a new project
Hot reload
Importing files and packages
Using widgets and creating your own
Making network calls
Showing items in a list
Adding an app theme
顺带着你也会学习一点关于Dart
的知识。
Setting up your development environment
Flutter
开发可以在macOS
,Linux
或Windows
上完成。 尽管您可以将任何编辑器与Flutter工具链一起使用,但是有 IntelliJ IDEA,Android Studio和Visual Studio Code的IDE
插件可以简化开发周期。 在本教程中,我们将使用VS Code
。
在此处here可以找到有关使用Flutter框架设置开发机器的说明。基本步骤因平台而异,但大多数情况是:
- 1) 下载适用于您开发计算机操作系统的安装包,以获取
Flutter SDK
的最新稳定版本 - 2) 将安装包解压缩到所需位置
- 3) 将flutter工具添加到您的路径
- 4) 运行
flutter doctor
命令,该命令将安装Flutter框架(包括Dart
)并提醒您任何缺少的依赖项 - 5) 安装缺少的依赖项
- 6) 使用Flutter插件/扩展程序设置您的IDE
- 7) 测试驱动一个应用
Flutter网站上提供的说明做得很好,可以让您轻松地在所选平台上设置开发环境。本教程的其余部分假定您已经为Flutter开发设置了VS Code
,并且已经解决了flutter doctor
发现的所有问题。
如果您使用的是Android Studio
,那么您也应该能够很好地遵循。您还需要运行iOS模拟器,Android模拟器,或者已设置预配置的iOS设备或Android设备进行开发。
注意:要在iOS模拟器或iOS设备上进行构建和测试,您需要使用已安装Xcode的
macOS
。
Creating a new project
在安装了Flutter扩展的VS Code
中,通过选择View ► Command Palette…
或在macOS上单击Cmd-Shift-P
或在Linux或Windows上单击Ctrl-Shift-P
来打开命令面板。 在面板中输入Flutter:New Project
,然后按回车键。
输入项目的名称ghflutter
,然后按回车键。 选择一个文件夹来存储项目,然后等待Flutter在VS Code
中设置项目。 项目准备就绪后,将在编辑器中打开文件main.dart
。
在VS Code
中,您会在左侧看到一个面板,该面板显示您的项目结构。 有适用于iOS和Android的文件夹,还有一个包含main.dart
的lib
文件夹,并且具有适用于两个平台的代码。 仅在本教程中,您将在lib
文件夹中工作。
用以下内容替换main.dart
中的代码:
import 'package:flutter/material.dart';
void main() => runApp(GHFlutterApp());
class GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'GHFlutter',
home: Scaffold(
appBar: AppBar(
title: Text('GHFlutter'),
),
body: Center(
child: Text('GHFlutter'),
),
),
);
}
}
顶部附近的main()
函数对单个行函数使用=>
运算符来运行该应用程序。 您有一个名为GHFlutterApp
的应用程序类。
您在这里看到您的应用程序本身是一个StatelessWidget
。 Flutter应用程序中的大多数实体都是无状态或有状态的widgets
。 您可以覆盖widgets
的build()
方法来创建应用widgets
。 您正在使用MaterialApp
widgets
,该widgets
提供了Material Design
之后的应用所需的许多组件。
对于本入门教程,通过右键单击,选择Delete
选项,然后确认删除,从项目中删除test
文件夹中的测试文件widget_test.dart
。
如果您使用的是macOS
,请启动iOS模拟器。 您也可以在macOS,Linux或Windows上使用Android模拟器。 如果iOS模拟器和Android模拟器都在运行,则可以使用VS Code
窗口右下方的菜单在它们之间进行切换:
要构建和运行项目,您需要首先设置启动配置。
通过单击左侧面板上的crossed bug
图标,切换到Debug View
。
您会注意到,到目前为止,尚未定义任何配置。 单击No Configuration
以获取下拉列表并选择Add Configuration
VS Code
将创建一个launch.json
文件,其详细信息如下:
注意:选择
Add Configuration
项后,将自动为您生成此文件。 在本教程中,您无需修改它。
现在,您已经完成所有工作,可以通过按F5
或选择Debug ► Start Debugging
或单击绿色的播放图标来构建和运行项目。 您会看到Debug Console
已打开,并且如果在iOS上运行,则会看到用于构建项目的Xcode。 如果在Android上运行,则会看到Gradle
被调用以进行构建。
这是在iOS模拟器中运行的应用程序:
下面,在Android模拟器中运行:
您看到的慢速模式banner
表明该应用程序正在调试模式下运行。
您可以通过单击VS Code
窗口顶部工具栏右侧的停止按钮来停止正在运行的应用程序:
通过单击VS Code
左上方的图标或选择View ► Explorer
,可以返回项目视图。
Hot Reload
Flutter
开发的最佳方面之一是能够在进行更改时热重新加载您的应用程序。 这类似于Android Studio
的Instant Run/Apply Changes
。
构建并运行该应用程序,使其在模拟器或模拟器上运行:
现在,无需停止正在运行的应用程序,请将应用程序栏字符串更改为其他内容:
appBar: AppBar(
title: Text('GHFlutter App'),
),
现在,单击工具栏上的热重载按钮或直接保存main.dart
文件:
一两秒钟之内,您应该会看到正在运行的应用程序中反映出的更改:
热重载功能可能并不总是有效,官方文档official docs可以很好地解释无法使用的情况,但总体而言,在构建UI时可节省大量时间。
Importing a File
您将希望能够从创建的其他类中导入代码,而不是将所有Dart代码都保存在单个main.dart
文件中。 现在,您将看到一个导入字符串的示例,该示例在需要本地化面向用户的字符串时会有所帮助。
右键单击lib
并选择New File
,在lib
文件夹中创建一个名为strings.dart
的文件:
将以下类添加到新文件中:
class Strings {
static String appTitle = "GHFlutter";
}
将以下import
添加到main.dart
的顶部
import 'strings.dart';
更改widget
以使用新的字符串类,以便GHFlutterApp
类如下所示:
class GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: Strings.appTitle,
home: Scaffold(
appBar: AppBar(
title: Text(Strings.appTitle),
),
body: Center(
child: Text(Strings.appTitle),
),
),
);
}
}
按下F5
键即可构建并运行该应用,您应该不会有任何变化,但是现在您正在使用字符串文件中的字符串。
Widgets
Flutter
应用程序中的几乎每个元素都是widget
。 widget
被设计为不可变的,因为使用不可变的widget
有助于使应用程序UI保持轻便。
您将使用两种基本类型的小部件:
-
Stateless - 无状态:仅依赖于自己的配置信息的
widget
,例如图像视图中的静态图像。 -
Stateful - 有状态:需要维护动态信息并通过与
State
对象进行交互来实现的信息。
无状态小部件和有状态widget
都在Flutter
应用程序中的每帧上重新绘制,不同之处在于,有状态widget
将其配置委托给State
对象。
要开始制作自己的widget
,请在main.dart
底部创建一个新类:
class GHFlutter extends StatefulWidget {
@override
createState() => GHFlutterState();
}
您已经创建了StatefulWidget
子类,并且您将覆盖createState()
方法以创建其状态对象。 现在,在GHFlutter
上方添加GHFlutterState
类:
class GHFlutterState extends State<GHFlutter> {
}
GHFlutterState
使用GHFlutter
的参数扩展State
。
制作widget
时的主要任务是覆盖将widget
呈现到屏幕时调用的build()
方法。
在GHFlutterState
中添加一个build()
重写:
@override
Widget build(BuildContext context) {
}
填写build()
如下:
@override
Widget build(BuildContext context) {
return Scaffold (
appBar: AppBar(
title: Text(Strings.appTitle),
),
body: Text(Strings.appTitle),
);
}
Scaffold
是用于材料设计widgets
的容器。 它充当widgets
层次结构的根。 您已在Scaffold
中添加了一个AppBar
和一个body
,每个都包含一个Text widget
。
更新GHFlutterApp
,使其使用新的GHFlutter
小部件作为其home
属性,而不是构建自己的支架:
class GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: Strings.appTitle,
home: GHFlutter(),
);
}
}
构建并运行该应用程序,您将看到新的widget
在起作用:
尚未发生太大变化,但是现在您可以设置以构建新的widget
。
Making Network Calls
之前,您已将strings.dart
文件导入到项目中。 您可以类似地导入Flutter
框架和Dart
中包含的其他软件包。
例如,您现在将使用框架中可用的包进行HTTP
网络调用,并将生成的响应JSON
解析为Dart
对象。 在main.dart
顶部添加两个新导入:
import 'dart:convert';
import 'package:http/http.dart' as http;
您会注意到http
包不可用。 这是因为尚未将其添加到项目中。 导航到pubspec.yaml
文件,然后在dependencies
和cupertino_icons:^ 0.1.2
下添加以下内容:
cupertino_icons: ^0.1.2
# HTTP package
http: ^0.12.0+2
注意:注意缩进。 保持 http 软件包声明的缩进与
cupertino_icons
软件包的缩进相同。
现在,当您保存pubspec.yaml
文件时,VS Code
中的Flutter
扩展名将运行flutter pub get
命令。 Flutter将获得声明的http
软件包,您的软件包也将在main.dart
中可用。
现在,您将在main.dart
中看到有关当前未使用的导入的指示器。
Dart
应用程序是单线程的,但是Dart提供了对在其他线程上运行代码以及运行异步代码的支持,这些异步代码不会使用async / await
模式阻止UI线程。
您将进行异步网络调用以检索GitHub
团队成员的列表。 在GHFlutterState
的顶部添加一个空列表作为属性,还添加一个属性以容纳文本样式:
var _members = [];
final _biggerFont = const TextStyle(fontSize: 18.0);
名称开头的下划线使该类的成员成为私有成员。
要进行异步HTTP
调用,请向GHFlutterState
添加方法_loadData()
:
_loadData() async {
String dataURL = "https://api.github.com/orgs/raywenderlich/members";
http.Response response = await http.get(dataURL);
setState(() {
_members = json.decode(response.body);
});
}
您已经在_loadData()
上添加了async
关键字,以告知Dart
它是异步的,并且还在http.get()
调用上阻塞了await
关键字。 您使用的dataUrl
值设置为GitHub API
端点,该端点检索GitHub
组织的成员。
HTTP调用完成后,您将向回调传递给setState()
,该回调在UI线程上同步运行。 在这种情况下,您将解码JSON响应并将其分配给_members
列表。
将initState()
重写添加到GHFlutterState
,该状态在初始化状态时调用_loadData()
:
@override
void initState() {
super.initState();
_loadData();
}
Using a ListView
现在您已经有了Dart
成员列表,您需要一种在UI列表中显示它们的方法。 Dart
提供了一个ListView
widget
,可让您在列表中显示数据。 ListView
的行为类似于Android
上的RecyclerView
和iOS上的UITableView
,在用户滚动列表以实现平滑滚动性能时回收视图。
将_buildRow()
方法添加到GHFlutterState
中:
Widget _buildRow(int i) {
return ListTile(
title: Text("${_members[i]["login"]}", style: _biggerFont)
);
}
您将返回一个ListTile widget
,该widget
显示从ith
成员的JSON解析的login
值,并使用您之前创建的文本样式。
更新GHFlutterState
的构建方法,使其主体为ListView.builder
:
body: ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: _members.length,
itemBuilder: (BuildContext context, int position) {
return _buildRow(position);
}),
你已经添加padding
,itemCount
设置为成员的数量,并使用_buildRow()
为给定的位置设置itemBuilder
。
您可以尝试热重载,但可能会收到“Full restart may be required”
消息。 如果是这样,请按F5键构建并运行该应用程序:
进行网络通话,解析数据并在列表中显示结果就是这么简单!
Adding dividers
要将分隔符添加到列表中,您需要将item
数量加倍,然后在列表中的位置为奇数时返回Divider widget
。 如下更新GHFlutterState
的构建方法:
body: ListView.builder(
itemCount: _members.length * 2,
itemBuilder: (BuildContext context, int position) {
if (position.isOdd) return Divider();
final index = position ~/ 2;
return _buildRow(index);
}),
确保不要错过itemCount
上的* 2
。 有了分隔线后,您已经从构建器中删除了padding
。 在itemBuilder
中,您要么返回Divider()
,要么通过整数除法并使用_buildRow()
来构建行项目来计算新索引。
尝试热重载,您应该在列表上看到分隔线:
要将padding
重新添加到每一行中,您想在_buildRow()
中使用Padding widget
:
Widget _buildRow(int i) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: ListTile(
title: Text("${_members[i]["login"]}", style: _biggerFont)
)
);
}
ListTile
现在是padding widget
的子widget
。 热重新加载以查看行上的padding
,而不是分隔线上的padding
。
Parsing to Custom Types
在上一节中,JSON解析器将JSON响应中的每个成员作为Dart Map
类型添加到_members
列表中,相当于Kotlin
中的Map
或Swift
中的Dictionary
。
但是,您还希望能够使用自定义类型。
在main.dart
文件中添加一个新的Member
类型:
class Member {
final String login;
Member(this.login) {
if (login == null) {
throw ArgumentError("login of Member cannot be null. "
"Received: '$login'");
}
}
}
成员具有login
属性和一个构造函数,如果登录值为null
,则该构造函数将抛出错误。
更新GHFlutterState
中的_members
声明,以便它是Member
对象的列表:
var _members = <Member>[];
更新_buildRow()
以在Member
对象上使用login
属性,而不是使用映射上的login
键:
title: Text("${_members[i].login}", style: _biggerFont)
现在,更新发送到_loadData()
中的setState()
的回调,以将解码后的映射转换为Member
对象并将其添加到成员列表中:
setState(() {
final membersJSON = json.decode(response.body);
for (var memberJSON in membersJSON) {
final member = Member(memberJSON["login"]);
_members.add(member);
}
});
如果尝试进行热重装,您可能会看到一个错误,但是停止并按F5键来构建和运行该应用,您应该会看到与以前相同的屏幕,除了现在使用新的Member
类。
Downloading Images with NetworkImage
来自GitHub
的每个成员都有其头像的URL。 现在,您将该头像添加到Member
类中,并在应用程序中显示头像。
更新Member
类以添加一个avatarUrl
属性,该属性不能为null
:
class Member {
final String login;
final String avatarUrl;
Member(this.login, this.avatarUrl) {
if (login == null) {
throw ArgumentError("login of Member cannot be null. "
"Received: '$login'");
}
if (avatarUrl == null) {
throw ArgumentError("avatarUrl of Member cannot be null. "
"Received: '$avatarUrl'");
}
}
}
使用NetworkImage
和CircleAvatar
widget
更新_buildRow()
以显示头像:
Widget _buildRow(int i) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: ListTile(
title: Text("${_members[i].login}", style: _biggerFont),
leading: CircleAvatar(
backgroundColor: Colors.green,
backgroundImage: NetworkImage(_members[i].avatarUrl)
),
)
);
}
通过将头像设置为ListTile
的leading
属性,它将在行内标题之前显示。 您还使用Colors
类在图片上设置了背景色。
现在更新_loadData()
以在创建新Member
时使用映射中的“ avatar_url”
值:
final member = Member(memberJSON["login"], memberJSON["avatar_url"]);
使用F5停止,构建和运行该应用程序。 您会在每一行中看到您的成员头像:
Cleaning the Code
现在,您的大多数代码都位于main.dart
文件中。 为了使代码更简洁,您可以重构已添加到文件中的widget
和其他类。
在lib
文件夹中创建名为member.dart
和ghflutter.dart
的文件。 将Member
类移至member.dart
,并将GHFlutterState
和GHFlutter
类移至ghflutter.dart
。
您在member.dart
中不需要任何import
语句,但是ghflutter.dart
中的导入应如下所示:
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'member.dart';
import 'strings.dart';
您还需要更新main.dart
中的导入,以便整个文件包含以下内容:
import 'package:flutter/material.dart';
import 'ghflutter.dart';
import 'strings.dart';
void main() => runApp(GHFlutterApp());
class GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: Strings.appTitle,
home: GHFlutter(),
);
}
}
按下F5来构建和运行该应用程序,您应该看不到任何更改,但是代码现在更简洁了。
Adding a Theme
您可以通过将theme
属性添加到您在main.dart
中创建的MaterialApp
中,轻松地将主题添加到应用中:
class GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: Strings.appTitle,
theme: ThemeData(primaryColor: Colors.green.shade800),
home: GHFlutter(),
);
}
}
您将绿色用作主题的“材料设计”颜色值。
按下F5来构建和运行应用程序,以查看新的主题:
大多数应用程序屏幕截图均来自Android模拟器。 您还可以在iOS模拟器中运行最终的主题应用程序:
这就是我所说的跨平台!
有关Flutter
和Dart
的知识还有很多。 最好的起点是:
-
flutter.dev上的
Flutter
主页。 您会发现很多很棒的文档和其他信息。 - 在此处here查看可用的
widgets
。 - 这里here有一个很好的指南供
Android
开发人员过渡到使用Flutter
。 - 适用于
React Native
开发人员的类似指南在这里here。
后记
本篇主要讲述了Flutter 入门,感兴趣的给个赞或者关注~~~