想象一下,你是一名怀揣着梦想的开发者,渴望开发出一款能够在多个平台上流畅运行的移动应用。然而,传统的开发方式需要为不同的平台编写不同的代码,这不仅耗费大量的时间和精力,还容易出现兼容性问题。就在你感到困惑和迷茫的时候,Flutter 出现了,它就像一道曙光,为你照亮了前行的道路。
Flutter 是 Google 推出的一款开源 UI 框架,它允许开发者使用一套代码库来为多个平台开发移动应用,包括 iOS 和 Android。使用 Flutter,你可以告别繁琐的多平台开发,专注于实现应用的功能和设计。
在开始学习 Flutter 之前,我们需要做好一些准备工作。首先是搭建开发环境,这就像是为我们的开发之旅打造一艘坚固的船。搭建开发环境的步骤并不复杂,下面为你详细介绍:
-
安装 Dart SDK:Flutter 使用 Dart 语言开发,所以我们需要先安装 Dart SDK。你可以从 Dart 官网下载并安装最新版本的 Dart SDK。
-
安装 Flutter SDK:通过 Flutter 官网下载 Flutter SDK,并按照平台特定的安装指南进行安装。
-
配置环境变量:
- 在 Linux 和 macOS 系统中,编辑
~/.bashrc或~/.zshrc文件,添加以下内容:
- 在 Linux 和 macOS 系统中,编辑
export PATH="$PATH:/path/to/flutter/bin
注意:在 Windows 系统中,打开环境变量设置,将 Flutter SDK 路径添加到 PATH 环境变量中。
鼠标右击选择【属性】->【系统信息】-> 高级系统设置
选择环境变量
选择环境变量->选择path
将flutter的bin的路径添加到path
- 安装 Flutter IDE:建议使用 VS Code 作为 Flutter 开发 IDE,并安装 Flutter 相关的插件。
- 安装其他必备工具:安装 Android Studio 和 Xcode,并配置好 Android SDK 和 iOS SDK。
安装完成后,在终端运行 flutter doctor 命令来检查 Flutter 和 Dart 的安装是否成功。
当开发环境搭建好后,我们就可以开启第一个 Flutter 项目了。在终端使用以下命令创建一个新的 Flutter 项目:
flutter create my_first_flutter_app
cd my_first_flutter_app
flutter run
运行上述命令后,你将在默认浏览器中看到 Flutter 应用的预览页面,这标志着你正式踏上了 Flutter 开发的征程。
变量及常量
变量
变量是存储数据的容器,其值可以在程序运行过程中改变。在 Dart 里,使用 var 关键字来声明变量。
var
常量
常量是在程序运行过程中值不能被改变的量,Dart 中有两种常量声明方式
| 常量 | 区别 |
|---|---|
| final | 编译时不可修改 |
| const | 运行时不可修改 |
代码演示如下:
void main() {
// 变量 var
var age = 20;
age = 21;
print(age);
// 常量 const
const number = 3.1415926;
const length = number * 2 * 10;
print(length);
// 常量 final
final time = DateTime.now();
prnt(time);
}
数据类型
Int
整型类型 用于表示整数,取值范围为 -2^53 到 2^53,不能包含小数点。例如:int age = 25;
Double
浮点类型 表示 64 位(双精度)浮点数,用于处理带有小数点的数字。例如:double price = 9.99;
int 和 double 都是 num 类型的子类,num 类型支持的运算操作有 +、-、*、/ 以及移位操作 >> 等,常用方法有 abs()、ceil() 和 floor() 等
String
用于表示文本信息,可以使用单引号 ' 或双引号 " 来定义,也可以使用单引号或双引号的三引号来创建多行文字。
String s1 = '单引号定义字符串';
String s2 = "双引号定义字符串";
String s3 = '''这是一个
多行的
文本''';
String s4 = """这也是一个
多行的
文本""";
字符串还支持插值操作,类似于 Kotlin 字符串模板,例如:
int num = 10;
String message = "数字是 $num";
- Boolean 类型:只有两个值
true和false,用于表示逻辑判断的结果。例如:bool isChecked = true;
集合类型
- List 类型:是一种有序容器,其中可以包含任何类型的元素,列表里的数据通常要求是同一类型,下标从 0 开始。
- 字面量方式创建:
List<int> numbers = [1, 2, 3, 4, 5];- 构造方式创建:
List<int> list = List.empty(growable: true); list.add(1);- 扩展操作符:
var list1 = [1, 2, 3]; var list2 = [0, ...list1]; - Map 类型:是一种无序容器,将
Key和Value关联在一起,也就是键值对,Key必须是唯一的。- 字面量方式创建:
Map<String, int> ageMap = {"John": 30, "Jane": 25};- 构造方式创建:
var weekMap = new Map(); weekMap['Monday'] = '星期一'; - Set 类型:是一种无序容器,其中每个元素都是唯一的。
Set<int> numbersSet = {1, 2, 3, 4, 5}; numbersSet.add(6);
其他类型
- Dynamic 类型:可以存储任何类型的值,不限于上述类型。但是,在使用动态类型时需要更加小心,因为编译器无法检查类型错误。
dynamic value = 10; value = "hello"; - Symbol 类型:在 Dart 中,
Symbol类型表示编译时常量标识符,通常用于反射。例如:const symbol = #mySymbol;
特殊值
在 Dart 中,使用 null 表示空值或不存在的值,当变量未被赋值时,默认值为 null。例如:
int? number;
print(number); // 输出: null
自定义组件
无状态组件 [StatelessWidget]
- 无状态组件是不可变的,意味着它们的属性不能改变, 所有的值都是最终的。
- 通常用于当你需要展示的UI不依赖于对象内部状态时。
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(MainPage());
}
class MainPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
title: "无状态组件",
home:Scaffold(
appBar: AppBar(
title:Text("顶部区域"),
),
body: Container(
child: Center(
child: Text("中部本区域"),
),
),
bottomNavigationBar: Container(
height: 80,
child: Center(
child: Text("底部区域"),
),
)
)
);
}
}
有状态组件 [StatefulWidget]
- 有状态组件可以在其生命周期中改变状态。
- 通常用于当UI可以在用户交互或其他因素影响下改变时
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(MainPage());
}
// 对外接受和定义最终参数
class MainPage extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return _MainPage();
}
}
// 负责管理数据 处理业务逻辑 渲染视图
class _MainPage extends State<MainPage>{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
title: "有状态组件",
home:Scaffold(
appBar: AppBar(
title:Text("顶部区域"),
),
body: Container(
child: Center(
child: Text("中部本区域"),
),
),
bottomNavigationBar: Container(
height: 80,
child: Center(
child: Text("底部区域"),
),
)
)
);
}
}
使用Awesome Flutter Snippets用来创建有状态组件及无状态组件的快捷键
无状态组件:statelessW
有状态组件:statefulW
代码示例
以下是无状态组件代码示例
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(StateLessWeight());
}
class StateLessWeight extends StatelessWidget {
const StateLessWeight({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
child: null,
);
}
}
以下部分是有状态组件代码示例
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(TestWeight());
}
class TestWeight extends StatefulWidget {
TestWeight({Key? key}) : super(key: key);
@override
_TestWeightState createState() => _TestWeightState();
}
class _TestWeightState extends State<TestWeight> {
@override
Widget build(BuildContext context) {
return Container(
child: null,
);
}
}
组件的生命周期
无状态组件
存在唯一阶段:build,当组件被创建或者父组件的状态发生变化,导致下面的组件发生变化并需要重新构建时,此方法会被调用
代码示例
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(MainPage());
}
class MainPage extends StatelessWidget {
const MainPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
print("无状态组件的执行");
return MaterialApp(
home: Scaffold(
body: Container(
child: Center(
child: Text("无状态"),
),
),
),
);
}
}
有状态组件
build阶段的生命周期
stateDiagram-v2
createState --> initState
initState --> didChangeDependencies
didChangeDependencies --> build
组件创建 --> 组件初始化
组件初始化 --> 更改依赖项
更改依赖项 --> 构建
update阶段的生命周期
stateDiagram-v2
父组件重建_配置变更 --> didUpdateWidget
didUpdateWidget --> build
销毁阶段的生命周期
stateDiagram-v2
组件移除 --> deactivate
deactivate --> dispose
生命周期函数
| 生命周期阶段 | 函数名 | 调用时机与核心任务 |
|---|---|---|
| 创建阶段 | createState() | Widget初始化调用,创建State对象 |
| initState() | state对象插入Widget立即执行,仅执行一次 | |
| didChangeDependencies() | initState后立刻执行,当所依赖的inheritedWidget更新时调用,可能多次 | |
| 构建与更新阶段 | build() | 创建UI方法,初始化或更新后多次调用 |
| didupdateWidget() | 父级组件传入新配置时调用,用于比较新旧配置 | |
| 销毁阶段 | deactiveate() | 当State对象从树中暂时移除时调用 |
| dispose() | 释放资源 |
执行一次函数: createState initState dispose
lnheritedWidget
专门用于在widget树中自定向下高效进行信息共享,其中顶部组件负责提供数据 子级数据负责进行数据的获取。
事件-点击事件
GestureDetector 用户与应用程序交互时触发的各种动作 特征:屏幕的滑动 点击及触摸
常用方法及说明
| 常用方法 | 说明 |
|---|---|
| OnTap() | 单击事件 |
| OnDoubleTap() | 屏幕双击事件 |
代码演示
单击事件
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(MainPage());
}
class MainPage extends StatelessWidget {
const MainPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "点击事件演示",
home: Scaffold(
appBar: AppBar(
title: Text("标题区域"),
),
body: Container(
child: Center(
child: GestureDetector(
child: Text("测试中部区域"),
// 单击事件
onTap: (){
print("测试区域");
},
)
)
),
bottomNavigationBar: Container(
height: 80,
child: Center(
child: Text("底部区域"),
),
),
),
);
}
}
双击事件
此段代码仅展示上段代码修改区域
// 单击事件
// onTap: (){
// print("测试区域");
// },
// 双击事件
onDoubleTap: (){
print("双击该区域");
}
长按事件
// 单击事件
// onTap: (){
// print("测试区域");
// },
// 双击事件
// onDoubleTap: (){
// print("双击该区域");
// },
onLongPress: (){
print("长按该区域");
},
组件点击事件
常用方法及说明
flutter提供多种方式为组件添加点击交互
| 组件类别 | 核心组件 | 组件说明 |
|---|---|---|
| 专用按钮组件 | ElevatedButton、TextButton、OutlineButton、FloatingActionButton | 内置点击动画和样式,通过onPressed参数处理点击逻辑 |
| 视觉反馈组件 | lnkwell | 提供点击事件(onTap) 有MaterialDesign风格的水纹扩散效果 |
| 其他交互组件 | IconButton、Switch、checkbox | 具有特定交互功能的交互式控件,点击事件(onPressed) |
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(MainPage());
}
class MainPage extends StatelessWidget {
const MainPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "点击事件演示",
home: Scaffold(
appBar: AppBar(
title: Text("标题区域"),
),
body: Container(
child: Center(
child: GestureDetector(
// TextButton组件
child: TextButton(
onPressed: (){
print("TextButton测试");
},
child: Text("按钮测试")
),
)
)
),
bottomNavigationBar: Container(
height: 80,
child: Center(
child: Text("底部区域"),
),
),
),
);
}
}
OutlinedButton代码演示
body: Container(
child: Center(
child: GestureDetector(
child: OutlinedButton(onPressed: (){
print("数据");
}, child: Text("数据"))
),
)
),
状态更新setState
定义
在 Flutter 里,setState 是 State 类的一个方法,其作用是通知 Flutter 框架当前组件的状态已经发生了变化,需要重新构建 UI。当调用 setState 方法时,Flutter 会调用 State 对象的 build 方法来重新生成 UI 界面。其定义形式通常如下
void setState(VoidCallback fn) {
// 内部实现逻辑,通知框架状态改变 // 然后触发 build 方法重新构建 UI
}
这里的 fn 是一个回调函数,在这个回调函数中可以修改组件的状态变量。
原理
- 状态改变触发:当在
State类中调用setState方法时,需要传入一个回调函数。在这个回调函数内部对组件的状态变量进行修改。例如:
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int counter = 0;
void _incrementCounter() {
setState(() { counter++; });
}
@override
Widget build(BuildContext context) {
return Column( children: [ Text('Counter: $counter'), ElevatedButton( onPressed: _incrementCounter, child: Text('Increment'), ), ], );
}
}
在上述代码中,点击 ElevatedButton 会调用 _incrementCounter 方法,在该方法里调用了 setState 并在其回调函数中修改了 counter 的值。
- 标记组件为脏组件:调用
setState后,Flutter 框架会将当前的State对象标记为“脏”(dirty)。这意味着该组件的状态已经改变,需要重新构建 UI。 - 重新构建 UI:在后续的帧构建过程中,Flutter 框架会遍历所有标记为“脏”的组件,并依次调用它们的
build方法。在build方法中,会使用更新后的状态变量来创建新的Widget树。 - 渲染更新:新的
Widget树会与旧的Widget树进行比较(通过Key和runtimeType等),找出有差异的部分,然后只更新那些有变化的部分到屏幕上,从而实现 UI 的更新。
总结来说,setState 的原理就是通过标记组件状态改变,触发 build 方法重新构建 UI,再通过比较新旧 Widget 树来高效地更新屏幕显示。
代码演示
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(mainPage());
}
// 使用无状态组件
class mainPage extends StatefulWidget {
mainPage({Key? key}) : super(key: key);
@override
_mainPageState createState() => _mainPageState();
}
class _mainPageState extends State<mainPage> {
// 数据定义
int count = 3;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Row(
children: [
TextButton(onPressed: (){
if(count==0){
count=0;
}else{
setState(() {
count-=1;
});
}
}, child: Text("减")),
Text(count.toString()),
TextButton(onPressed: (){
// UI 数据更新
setState(() {
count+=1;
});
}, child: Text("加")),
],
),
),
),
);
}
}