- 环境搭建
- flutter项目目录
- dart基础语法
- flutter基础组件使用
- 写demo
- 深入
一. Flutter项目目录
二. Dart基础语法
-
dart在声明变量时,需要先声明数据类型
-
数据类型的存在是为了解决:
如何在代码中表示数据种类的问题
1. 数据类型
-
Object 类型
- 对dart来讲,所有的对象都是继承于object类型
- 声明方式 var 会自动根据后面内容,自动生成类型
-
dynamic 动态类型
-
常量
- 声明方式为:
- const 为编译时常量
- final 为运行时常量
-
变量
- int double数字类型
- String字符串类型
- bool布尔型
- 通过
$变量名
可以在字符串内("")插入变量值- 可以通过
${表达式}
来嵌入表达式
2. 函数
(通过函数可以将特定的算法封装)
- 默认值
int addAge(int age1,[int age2]){}
这个中括号意思是,这个参数是可传可不传的(可选参数)但是并不建议这样写,因为如果这样写然后函数里又用到了这个参数,就必须给这个参数加三目运算
所以可以把中括号的方式替换成 赋初值 的方式(但是这种给初始值的方式,需要额外做的是,给参数外面括上大括号,不然报错)
int addAge({int age1,int age2=0}){}
建议用赋初值(默认值) 而不是中括号
- 命名参数
- Dart 中支持命名参数,可以通过参数的名称来传参,不需要在意入参的顺序。通过
{}
包裹命名的参数,其中 required 关键字表示该入参必须传入;👇
double bmi({
required double height,
double weight = 65,
}) {
// 具体算法
double result = weight / (height * height);
return result;
}
则在使用时可以通过 weight
和 height
指定参数
void main() {
double toly = bmi(weight: 70, height: 1.8);
double ls = bmi(height: 1.79);
double wy = bmi(height: 1.69, weight: 50);
}
- 位置参数
- 方括号
[]
包围参数列表,位置参数可以给默认值。- 调用时必须按照参数顺序传入
- 构造函数赋初值(c++与dart的区别)
- 在 Dart 中,构造函数赋初值时,可以不写
this
关键字,前提是构造函数的参数名称与类的成员变量名称不冲突。当构造函数的参数名称与成员变量名称相同时,需要使用this
关键字来区分成员变量和参数。- 在 C++ 中,构造函数赋初值时不能使用
this
关键字来引用当前对象的成员变量- 还有一个更加简便的赋初值方法👇
class Human {
Human(this.name,this.weight,this.height);
}
3. 类的继承
- 可以使用关键字
extends
,通过继承来派生类型- (extends只能是单继承,多继承要用到混合mixin)
class Person with Eat,Sleep{
}
super.name
语法是:在入参中为父类中的成员赋值
class Student extends Human {
final String school;
// 写法一
Student(super.name,super.weight,super.height, {required this.school});
// 写法二
Student(int weight,int height,string school):super(name,weight,height){
this.school=school;
}
}
- 子类还可以覆写父类的方法
- 当子类中存在和父类同名的方法时
- 会优先使用子类方法,子类没有该方法时,才会触发父类方法
- 通过
super.
可调用父类方法; 一般子类覆写方法时,加@override
注解进行示意 (非强制)
3. 聚合类型(List,Map,Set)
可以称这样的数据为聚合类型、或容器类型
- 列表 List(允许重复,不能运算)
List<int> numList = [1,9,9,4,3,2,8];
// 方法
1. add是在末尾加入一个元素
2. insert 是在指定索引处插入一个元素
3. removeAt 移除指定索引处元素
4. remove 移除指定元素
5. removeLast 移除最后元素
6. .length 得到列表长度
//循环
1. for 循环
for (int i = 0; i < numList.length; i++) {
int value = numList[i];
print("索引:$i, 元素值:$value");
}
2. for in 循环
for(int value in numList){
print("元素值:$value");
}
- 集合 Set(不允许重复,集合间可以运算)
Set<int> numSet = {1, 9, 4};
// 方法
// 集合set没有索引概念
1. add 加元素
2. remove 移除元素
// 但是集合可以运算
1. difference 差集
2. union 并集
3. intersection 交集
// 循环
// 不能通过for循环索引来遍历,只能通过for in
for(int value in numSet){
print("元素值:$value");
}
- 映射 Map (key和value的对应关系)
Map<int, String> numMap = { 0: 'zero', 1: 'one', 2: 'two', };
// `numMap[key] = value` 语法可以向映射中添加元素,如果 key 已经存在,这个行为就是修改对应的值
// 循环
用forEach
numMap.forEach((key, value) {
print("${key} = $value");
});
4. 抽象类
- 就是在类名前加一个abstract
- 里面可以写抽象方法
- 定义好抽象方法之后,交给继承这个类的去实现
abstract class Animal{
// 只定义而没有实现,是抽象方法(也就是先把想法定义出来)
void baby();
// 方法实现了,所以不是抽象方法
void have_a_baby(){
print('have a baby');
}
}
5. 异步处理
6. 调用库
三. Flutter demo
TIP: 这种在开发过程中,不重新启动就可以更新界面的能力,称之为 热重载 hot reload 。不用每次都重新编译、启动,这可以大大提升开发的时间效率。
<猜数字>
1. 代码思路
一开始是main方法,表示程序的入口,创建了一个MyApp的对象,并将该对象传入runapp的函数中。
也就是说程序从这个main函数的runapp函数中开始运行程序。程序开始。
void main() {
runApp(const MyApp());
}
进入myapp这个widget类型对象之后,myapp它return返回的是MaterialApp
// myapp实际返回的是 MaterialApp对象
class MyApp extends StatelessWidget {
// myapp的构造函数
const MyApp({super.key}); // super用于调用父类的构造函数或方法
// 这个小部件是应用程序的根
// 覆写了build方法,返回的是Widget类型对象
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter demo',
theme: ThemeData(
// 这是应用程序的主题
// 主题颜色
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
// 这个MyHomePage部分单独放在外面了
home: const GuessPage(title: '猜数字'),
);
}
}
这个返回的MaterialApp 对象也是一个widget,而这个MaterialApp里也有home入参,它也将接收一个widget的派生类
@override
Widget build(BuildContext context) {
return MaterialApp(
title: ..
theme: ..,
// 这个MyHomePage部分单独放在外面了
home: const GuessPage(title: '猜数字'),
);
}
}
到此的逻辑如下:👇
接下来进入GuessPage类
class GuessPage extends StatefulWidget {
const GuessPage({super.key, required this.title});
final String title;
// createState 是 StatefulWidget 类中的一个方法,
// 用于创建与 StatefulWidget 关联的状态对象。
// 这个方法必须在子类中被重写,以返回一个新的状态对象。返回了一个实例
@override
State<GuessPage> createState() => _GuessPageState();
}
_GuessPageState类的状态在下面实现,这个类,重写build返回的是 Scaffold ,也就是我们的界面
- 本案例 Scaffold 分为了三个部分
- appBar头部(单独拿出去封装,为GuessAppBar)
- GuessAppBar有两个要传的参数,一个是输入的控制器
- 还有一个是最右边的确认的点击按钮(回调函数)
- body(用了Stack层层叠堆的布局)
- stack(下面是children包含的)
- Column 背景(“大了”“小了”,将采取新的小组件来专门封装这一部分,为ResultNotice)
- Center 文字(“点击随机生成随机数”)
- floatingActionButton 最底部的按钮
-GuessPageState的数据 | .. | ||
---|---|---|---|
_value | int类型 | 随机数 | .... |
_random | Random类型 | 生成随机数,之后赋值给_value | ... |
_guessing | bool布尔类型 | 表示游戏状态,控制按钮禁用 | true游戏开始,按钮禁用;false默认,游戏未开始 |
_isBig | bool类型 | 控制猜的结果的界面展示 | null相等,true大了,false小了 |
_guessCtrl | TextEditingController类型 | 输入控制器,一般使用这个类型的数据和输入框进行双向绑定 | ... |
- 其中appBar(头部)可以进行左中右的布局
- 并且由于头部代码比较复杂,功能也较多,所以我们将其封装一下,放在另一页管理,为
GuessAppBar
AppBar(
leading: 左侧,
actions: [右侧列表],
title: 中间部分,
)
guess_page.dart完整代码
class _GuessPageState extends State<GuessPage> {
// 随机数
int _value = 0;
// 生成随机数,到时候赋值给_value
final Random _random=Random();
// 按钮禁用(游戏开始标志)
// 规定false为按钮开启,不禁用
bool _guessing=false;
// 猜数字(这是个可空的布尔变量,所以下面在 后面加!意思是它不能为空)
bool?_isBig;
void _generateRandomValue() {
setState(() {
_guessing=true;
_value=_random.nextInt(100);// 不包含100
print(_value);
});
}
//在 Flutter 中,`dispose()` 方法用于清理资源,以避免内存泄漏
@override
void dispose(){
_guessCtrl.dispose();
super.dispose();
}
// 输入控制器
//常用于管理文本输入框 (TextField) 中的文本内容以及处理文本编辑的操作。它允许你监听文本内容的变化、设置初始文本、获取当前文本值等
TextEditingController _guessCtrl = TextEditingController();
// 猜数字
void _onCheck() {
print("=====Check:目标数值:$_value=====${_guessCtrl.text}============");
int? guessValue = int.tryParse(_guessCtrl.text);
// 游戏未开始,或者输入非整数,无视
if (!_guessing || guessValue == null) return;
//猜对了
if (guessValue == _value) {
setState(() {
_isBig = null;
_guessing = false;
});
return;
}
// 猜错了
setState(() {
_isBig = guessValue > _value;
});
}
@override
Widget build(BuildContext context) {
// 每次调用setState时都会重新运用此build方法
return Scaffold(
appBar: GuessAppBar(
onCheck:_onCheck,
controller: _guessCtrl,
),
body: Stack(
// Stack 层层叠堆
children: [
if(_isBig!=null)
Column(
children: [
if(_isBig!)
ResultNotice(color:Colors.redAccent,info:'大了'),
Spacer(),
if(!_isBig!)
ResultNotice(color:Colors.blueAccent,info:'小了'),
],
),
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
_guessing ?' ':'点击生成随机数值',
style: TextStyle(fontSize: 30),
),
Text(
_guessing ?'**':'$_value',
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
fontSize: 40, )// 设定你想要的字体大小
),
],
),
),
],
),
// _generateRandomValue 这个是点击生成随机数
// true就是null
floatingActionButton: FloatingActionButton(
onPressed: _guessing ? null : _generateRandomValue,
backgroundColor: _guessing ? Colors.grey : Colors.blue,
tooltip: 'Increment',
child: const Icon(Icons.generating_tokens_outlined),
), // 后面的逗号使得构建方法的自动格式化更加漂亮
);
}
}
2. 需求分析
- 生成随机数
- 通过random类型可以生成随机数
- 大了小了(通过stack层叠实现)
- 按钮禁用
- 在一次猜数字过程中,只能生成一个随机数;同时,猜对时,需要解除禁止,进入下一次游戏
- 对于生成随机数的需求,需要一个量来标识是否是在游戏过程中
- 数字隐藏,生成密文
- ——猜数字环节过程
- 如何获取输入的数字
- 点击确认利用回调函数来进行判断,并控制状态(按钮禁用,密文显示,背景变化)
- 动画,提高交互感