Dart

501 阅读9分钟

  1. 环境搭建
  2. flutter项目目录
  3. dart基础语法
  4. flutter基础组件使用
  5. 写demo
  6. 深入

一. Flutter项目目录

image.png

二. Dart基础语法

  • dart在声明变量时,需要先声明数据类型

  • 数据类型的存在是为了解决:

如何在代码中表示数据种类的问题

1. 数据类型

  1. Object 类型

    • 对dart来讲,所有的对象都是继承于object类型
    • 声明方式 var 会自动根据后面内容,自动生成类型
  2. dynamic 动态类型

  3. 常量

    • 声明方式为:
    • const 为编译时常量
    • final 为运行时常量
  4. 变量

    • int double数字类型
    • String字符串类型
    • bool布尔型
  • 通过 $变量名 可以在字符串内("")插入变量值
  • 可以通过 ${表达式} 来嵌入表达式

2. 函数

(通过函数可以将特定的算法封装)

  1. 默认值
  • int addAge(int age1,[int age2]){}这个中括号意思是,这个参数是可传可不传的(可选参数)

  • 但是并不建议这样写,因为如果这样写然后函数里又用到了这个参数,就必须给这个参数加三目运算

  • 所以可以把中括号的方式替换成 赋初值 的方式(但是这种给初始值的方式,需要额外做的是,给参数外面括上大括号,不然报错)int addAge({int age1,int age2=0}){}

  • 建议用赋初值(默认值) 而不是中括号


  1. 命名参数
  • 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);
}
  1. 位置参数
  • 方括号 [] 包围参数列表,位置参数可以给默认值。
  • 调用时必须按照参数顺序传入

  1. 构造函数赋初值(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)

可以称这样的数据为聚合类型、或容器类型

  1. 列表 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"); 
}
  1. 集合 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"); 
}
  1. 映射 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. 异步处理

Flutter - 掘金 (juejin.cn)

6. 调用库

三. Flutter demo

TIP: 这种在开发过程中,不重新启动就可以更新界面的能力,称之为 热重载 hot reload 。不用每次都重新编译、启动,这可以大大提升开发的时间效率。

<猜数字>

image.png

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: '猜数字'),
    );
  }
}

到此的逻辑如下:👇

image.png


接下来进入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 最底部的按钮

image.png

-GuessPageState的数据..
_valueint类型随机数....
_randomRandom类型生成随机数,之后赋值给_value...
_guessingbool布尔类型表示游戏状态,控制按钮禁用true游戏开始,按钮禁用;false默认,游戏未开始
_isBigbool类型控制猜的结果的界面展示null相等,true大了,false小了
_guessCtrlTextEditingController类型输入控制器,一般使用这个类型的数据和输入框进行双向绑定...
  • 其中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层叠实现)
  • 按钮禁用
    • 在一次猜数字过程中,只能生成一个随机数;同时,猜对时,需要解除禁止,进入下一次游戏
    • 对于生成随机数的需求,需要一个量来标识是否是在游戏过程中
  • 数字隐藏,生成密文
  • ——猜数字环节过程
  • 如何获取输入的数字
  • 点击确认利用回调函数来进行判断,并控制状态(按钮禁用,密文显示,背景变化)
  • 动画,提高交互感