前言
Flutter是Google开源的应用开发框架,仅通过一套代码,就能构建精美的、原生平台编译的多平台应用。在进入学习之前,我们需要先安装好运行环境,学习基本的Dart语法,了解Flutter项目的基本目录结构
入门
-
环境安装
-
项目运行
-
基础知识
学习方法
官方资料、视频教程、开源项目、参与技术分享及讨论
学习资料&传送门
| 名称 | 链接 | 相关说明 |
|---|---|---|
| Flutter问答 | flutterchina.club/faq/ | 了解Flutter |
| Dart官网 | dart.dev/ | 了解Dart |
| Dart开发者官网 | api.dart.dev/stable/2.15… | 学习Dart |
| Flutter官网 | flutter.cn/docs | 了解Flutter开发 |
| Flutter官方GitHub地址 | github.com/flutter | 学习Flutter开发 |
| Flutter实用教程 | flutter.cn/docs/cookbo… | 学习Flutter开发 |
| Flutter CodeLab | codelabs.flutter-io.cn/ | 学习Flutter开发 |
| Flutter中文网 | flutterchina.club/get-started… | 了解Flutter及学习更多知识 |
| Flutter-SDK | api.flutter-io.cn/ | Flutter API 参考文档 |
| Flutter安装环境 | flutter.cn/docs/get-st… | 获取Flutter-SDK,开发Flutter项目 |
| AndroidStudio | developer.android.com/studio | 一款Android&Flutter的开发工具 |
| pub.dev | pub.dev/flutter/pac… | 官方的插件平台,可用于上传自定义组件库,也可以用于学习或使用第三方组件库 |
| Flutter实战.第二版 | book.flutterchina.club/ | 学习资料 |
| awesome-flutter | github.com/Solido/awes… | 学习资料 |
| awesome-flutter/ | www.devio.org/2018/09/09/… | 学习资料 |
| Flutter老孟控件大全 | laomengit.com/flutter/wid… | 学习资料 |
| flutter-go | github.com/alibaba/flu… | 学习资料 |
| 咸鱼技术 | www.yuque.com/xytech/flut… | 学习资料 |
| 链家技术 | github.com/LianjiaTech… | 学习资料 |
| flutter_boost | github.com/alibaba/flu… | 混编插件 |
| FlutterDouBan | github.com/kaina404/Fl… | 学习项目 |
Flutter基础
目录结构
根据以下Flutter项目为例,展开项目的目录结构说说:
第一个应用程序
打开AndroidStudio>Create New Flutter Project>Flutter Application
创建项目完成之后,我们会发现在lib下面有一个main.dart文件,它是dart的执行程序入口,下面我们来看下项目创建的这段默认的代码:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
通过以上代码,我们会发现它依次运行
main()> runApp()>MyApp()>build()>MyHomePage>()>_MyHomePageState()>build()
其中MyApp继承了StatelessWidget类,
MyHomePage继承了StatefulWidget类,
_MyHomePageState继承了State,
_MyHomePageState类的build函数返回值是一个Widget对象:Scaffold(),而我们看到的页面正是这个Weidet
其中我们会发现,在每个Widget的build方法中都会有一个参数BuildContext参数,BuildContxt是用来传递各个Widget在Widget Tree中的具体位置,Flutter就是根据context包含的信息,来绘制各个Widget及其相对位置
生命周期
Flutter跟iOS和Android一样都拥有自己的生命周期,生命周期就是从创建到销毁的一个过程。在Flutter中一切都是Widget,所以它的生命周期分为两部分,一个是App的生命周期,另外一个就是Widget的生命周期。
App的生命周期
我们可以通过with WidgetsBindingObserver来监听App的生命周期,然后实现didChangeAppLifecycleState(AppLifecycleState state)回调函数,利用AppLifecycleState的枚举值来判断app的生命周期的状态。
程序入口:main函数只会执行一次,但是在退出应用程序之后会再次执行
void main() {
// 例如:在点击home键后重新进入app时不会被执行
// 在杀死app,或者点击返回键退出到桌面,或者执行SystemNavigator.pop()等情况后会再次执行
runApp(MyApp());
}
我们可以通过AppLifecycleState知道App的整个生命周期
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
var _tag = 'TagAppLife ';
@override
void initState() {
super.initState();
print(_tag + 'initState......');
// 添加生命周期监听
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
super.dispose();
print(_tag + 'dispose......');
// 移除生命周期监听
WidgetsBinding.instance.removeObserver(this);
}
/// 初次进入widget时,不执行AppLifecycleState的回调
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.resumed:
// 应用程序可见(从后台重新切换到前台时候被调用)
print(_tag + 'AppLifecycleState resumed');
break;
case AppLifecycleState.paused:
// 应用程序不可见(只要应用程式为不可见时都被会调用)
print(_tag + 'AppLifecycleState paused');
break;
case AppLifecycleState.inactive:
// 切换到后台时,随后pause也会被回调
print(_tag + 'AppLifecycleState inactive');
break;
case AppLifecycleState.detached:
// 应用程序仍然驻留在Flutter引擎上,在没有视图的情况下运行
print(_tag + 'AppLifecycleState detached');
break;
}
}
@override
Widget build(BuildContext context) {
return Scaffold();
}
}
Widget的生命周期
在Flutter中一切都是Widget,其中StatelessWidget和StatefulWidget是 flutter的基础组件。StatelessWidget是一个无状态的组件,用于那些始终不变的UI控件,通常自定义控件就会选择继承它;StatefulWidget是一个有状态的组件,可以通过改变状态使得 UI 发生变化,可以包含用户交互。
StatelessWidget的生命周期
StatelessWidget不需要维护状态,它通常在build方法中通过嵌套其它Widget来构建UI,对于StatelessWidget来说,生命周期只有他自己的构造函数和build函数。(先执行构造函数,后执行build函数)
class MyApp extends StatelessWidget {
// This widget is the root of your application.
MyApp() {
print('TagAppLife ' + '构造函数......');
}
@override
Widget build(BuildContext context) {
print('TagAppLife ' + 'build函数......');
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
StatefulWidget的生命周期
StatefulWidget拥有自己的状态,它是由Widget和State组成的,通过主动调用setState()函数可以通知框架内部发生改变,接着build函数就会被执行,重新构建widget树,从而达到界面刷新的目的。
-
createState: 在StatefulWidget创建State状态,只执行一次
-
reassemble: 点击闪电进行热重载时执行,仅用于debug调试,release版本不会执行该函数
-
initState: 该函数是StatefulWidget在被创建后调用的第一个方法,而且只执行一次,一般用于初始化操作
-
didChangeDependencies: 在initState回调函数执行之后立即调用,之后只有当StatefulWidget依赖的InheritedWidget发生变化之后,该函数才会被调用
-
build:主要是用于构建Widget子树,绘制界面,当主动调用setState方法后会被再次执行
-
didUpdateWidget: 重写此方法以在 [widget] 更改时做出响应
-
deactivate: 框架在移除这个 [State] 对象时调用这个方法,例如退出界面
-
dispose:组件被销毁,当这个对象从树中永久移除时调用,例如退出界面
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title});
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _tag = 'TagAppLife ';
_MyHomePageState() {
print(_tag + '构造函数......');
}
@override
void reassemble() {
super.reassemble();
// 点击闪电进行热重载时执行,仅用于debug调试,release版本不会执行该函数
// debug热重载时,initState函数不会被调用,可以通过此函数代替初始化
print(_tag + 'reassemble......');
}
@override
void initState() {
super.initState();
// 该函数是StatefulWidget在被创建后调用的第一个方法,而且只执行一次,一般用于初始化操作。
// 在执行该函数时,StatefulWidget的[mounted]值会变为true,
// 调用[dispose]之后框架将永远不会询问[State],StatefulWidget的mounted的值会变为false,
// 注意:除非[mounted]为真,否则直接调用[setState]是会报错的
print(_tag + 'initState......');
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// 在initState回调函数执行之后立即调用,之后只有当StatefulWidget依赖的InheritedWidget发生变化之后,该函数才会被调用
// InheritedWidget可以实现局部的子widget更新
print(_tag + 'didChangeDependencies......');
}
@override
Widget build(BuildContext context) {
// 主要是用于构建Widget子树,绘制界面,当主动调用setState方法后会被再次执行
print(_tag + 'build......');
return Scaffold();
}
@override
void didUpdateWidget(covariant MyHomePage oldWidget) {
super.didUpdateWidget(oldWidget);
// 重写此方法以在 [widget] 更改时做出响应
// 框架总是在调用[didUpdateWidget]后调用[build],即意味着在 [didUpdateWidget] 中对 [setState] 的任何调用都是多余的。
print(_tag + 'didUpdateWidget......');
}
@override
void deactivate() {
super.deactivate();
// 框架在移除这个 [State] 对象时调用这个方法,例如退出界面,调用了 Navigator.pop(context);
print(_tag + 'deactivate......');
}
@override
void dispose() {
super.dispose();
// 组件被销毁,当这个对象从树中永久移除时调用,例如退出界面,调用了 Navigator.pop(context);
print(_tag + 'dispose......');
}
}
基础知识
dart是由Google开发的计算机编程语言,它可以被用于web/服务器/移动应用/物联网等领取的开发。要学Flutter开发必须首先会dart。
变量规则
dart是一个强大的脚本语言,可以不预先定义变量类型,自动会类型推倒,定义变量可以通过var关键字声明变量,也可以通过具体类型来声明变量
例如:
var str = ' hello world';
String str = 'hello world';
dart中常量可以通过关键字const和final进行修饰,如何理解const和final之间的差别:
-
final、const必须初始化
-
final、const只能赋值一次
-
final修饰的List集合任意索引可以修改,const修饰的不可以修改
-
final可以开始不赋值,但是只能赋值一次,const一开始就得赋值
-
final可修饰实例变量、const不可以修饰实例变量
-
final不仅有const的编译时常量的特性,最重要的是它是运行时常量
-
final是惰性初始化,即在运行时第一次使用前才初始化
-
访问类中const修饰的变量需要static修饰
数据类型
dart支持的数据类型:int/double/string/bool/list/map ,int和double都是num类型的子类
运算表达式
算术运算符: +(加) -(减) *(乘) /(除) ~/(取整) %(取余)
关系运算符: ==(等于) !=(不等于) >(大于) <(小于) >=(大于等于) <=(小于等于)
逻辑运算符: !(非,取反) &&(并且) ||(或者)
基础赋值运算符:=(直接赋值) ??=(如果为空才赋值)
复合赋值运算符:+= -= *= /= %= ~/=
方法函数
Dart 没有 「public」「private」等关键字,默认就是public的,private请使用下划线 _开头。
/// 方法的作用域
void _test() {
// 方法里面可以再写方法
method1() {
print("111111");
method1_1() {
print("111111_111111");
}
// 方法里面可以再写方法
method1_1();
}
/// 可选参数,没有默认值。 {}代表需要命名参数
method2(String str, {bool flag}) {
print("222222");
}
method1();
method2("222222", flag: true);
method3("333333", true);
// 把方法当作参数传入
method4(method5);
}
/// 可选参数,并且附默认值。 []代表无需命名参数
method3(String str, [bool flag = false]) {
if (flag) {
method3_1(flag);
} else {
print("333333");
}
}
/// =>代表返回
var method3_1 = (bool flag) => print("333333_" + flag.toString());
method5() => print("555555");
method4(m()) {
print("444444");
m();
}
面向对象
Dart所有东西都是对象,所有的对象都继承object类。dart是一门使用类和单继承的面向对象语言,所有的对象都是类的实例,并且所有类都是obejct的子类。
class Person{
String _name = "";
int _age = 0;
/// 如果你定义了一个类,而没有定义构造函数,那么它将有一个默认的构造函数,这个构造函数没有参数
Person(this._name);
/// 命名构造函数,使用命名构造函数可以为类提供多个构造函数
/// 但是命名构造函数不可继承,如果子类想要有和父类一样的命名构造函数,那就写个同名的,通常也会在子类的命名构造函数里,调用父类的同名命名构造函数
Person.test1(String name,int age){
this._name = name;
this._age = age;
}
/// 命名构造函数(Dart的语法糖)
Person.test2(this._name, this._age);
int get getAge => _age;
set setAge(int value) {
_age = value;
}
String get getName => _name;
set setName(String value) {
_name = value;
}
@override
String toString() {
return 'Person{_name: $_name, _age: $_age}';
}
}
常用命令行
| flutter | 获取Flutter中所有的命令行帮助 |
|---|---|
| flutter doctor | 展示安装工具及环境配置等信息 |
| flutter --version | 查看flutter、dart版本信息 |
| flutter upgrade | 升级 Flutter SDK |
| flutter run | 运行flutter应用程序 |
| flutter run --release | 运行flutter应用程序(正式包) |
| flutter packages get | 获取项目中的依赖包(不包含flutter sdk) |
| flutter packages upgrade | 更新项目中的依赖包(不包含flutter sdk) |
| flutter packages outdated | 显示项目中过时的依赖包(不包含flutter sdk) |
| flutter logs | 查看应用程序运行日志 |
| flutter pub deps | 查看项目依赖树 |
| flutter analyze | 对项目进行静态代码检查,也是检验项目规范的一个标准 |
| flutter test | 对当前项目进行单元测试 |
| flutter build apk | 打包安卓项目 |
| flutter build ios | 打包iOS项目 |
| flutter build web | 构建应用程序以进行部署 |
| flutter format . | 格式化代码并自动移除未使用的导入包 |
| flutter build appbundle | 构建一个发布的 app bundle |
| flutter build ipa | 构建一个发布的 ipa |
| flutter build ipa --dart-define=xxx=true | 自定义环境构建一个发布的 ipa |
DevTools介绍
DevTools 是Flutter官方推出的一套适用于 Dart 和 Flutter 的性能和调试工具。了解更多
总结
万丈高楼平地起,一砖一瓦皆根基