Flutter核心技术与实战
陈航
美团点评高级技术专家
立即订阅
6150 人已学习
课程目录
已完结 47 讲
0/4登录后,你可以任选4讲全文学习。
课前必读 (3讲)
开篇词 | 为什么每一位大前端从业者都应该学习Flutter?
免费
01 | 预习篇 · 从0开始搭建Flutter工程环境
02 | 预习篇 · Dart语言概览
Flutter开发起步 (3讲)
03 | 深入理解跨平台方案的历史发展逻辑
04 | Flutter区别于其他方案的关键技术是什么?
05 | 从标准模板入手,体会Flutter代码是如何运行在原生系统上的
Dart语言基础 (3讲)
06 | 基础语法与类型变量:Dart是如何表示信息的?
07 | 函数、类与运算符:Dart是如何处理信息的?
08 | 综合案例:掌握Dart核心特性
Flutter基础 (13讲)
09 | Widget,构建Flutter界面的基石
10 | Widget中的State到底是什么?
11 | 提到生命周期,我们是在说什么?
12 | 经典控件(一):文本、图片和按钮在Flutter中怎么用?
13 | 经典控件(二):UITableView/ListView在Flutter中是什么?
14 | 经典布局:如何定义子控件在父容器中排版的位置?
15 | 组合与自绘,我该选用何种方式自定义Widget?
16 | 从夜间模式说起,如何定制不同风格的App主题?
17 | 依赖管理(一):图片、配置和字体在Flutter中怎么用?
18 | 依赖管理(二):第三方组件库在Flutter中要如何管理?
19 | 用户交互事件该如何响应?
20 | 关于跨组件传递数据,你只需要记住这三招
21 | 路由与导航,Flutter是这样实现页面切换的
Flutter进阶 (17讲)
22 | 如何构造炫酷的动画效果?
23 | 单线程模型怎么保证UI运行流畅?
24 | HTTP网络编程与JSON解析
25 | 本地存储与数据库的使用和优化
26 | 如何在Dart层兼容Android/iOS平台特定实现?(一)
27 | 如何在Dart层兼容Android/iOS平台特定实现?(二)
28 | 如何在原生应用中混编Flutter工程?
29 | 混合开发,该用何种方案管理导航栈?
30 | 为什么需要做状态管理,怎么做?
31 | 如何实现原生推送能力?
32 | 适配国际化,除了多语言我们还需要注意什么?
33 | 如何适配不同分辨率的手机屏幕?
34 | 如何理解Flutter的编译模式?
35 | Hot Reload是怎么做到的?
36 | 如何通过工具链优化开发调试效率?
37 | 如何检测并优化Flutter App的整体性能表现?
38 | 如何通过自动化测试提高交付质量?
Flutter综合应用 (6讲)
39 | 线上出现问题,该如何做好异常捕获与信息采集?
40 | 衡量Flutter App线上质量,我们需要关注这三个指标
41 | 组件化和平台化,该如何组织合理稳定的Flutter工程结构?
42 | 如何构建高效的Flutter App打包发布环境?
43 | 如何构建自己的Flutter混合开发框架(一)?
44 | 如何构建自己的Flutter混合开发框架(二)?
结束语 (1讲)
结束语 | 勿畏难,勿轻略
特别放送 (1讲)
特别放送 | 温故而知新,与你说说专栏的那些思考题
Flutter核心技术与实战
登录|注册

08 | 综合案例:掌握Dart核心特性

陈航 2019-07-16
你好,我是陈航。
在前两篇文章中,我首先与你一起学习了 Dart 程序的基本结构和语法,认识了 Dart 语言世界的基本构成要素,也就是类型系统,以及它们是怎么表示信息的。然后,我带你学习了 Dart 面向对象设计的基本思路,知道了函数、类与运算符这些其他编程语言中常见的概念,在 Dart 中的差异及典型用法,理解了 Dart 是怎么处理信息的。
可以看到,Dart 吸纳了其他编程语言的优点,在关于如何表达以及处理信息上,既简单又简洁,而且又不失强大。俗话说,纸上得来终觉浅,绝知此事要躬行。那么今天,我就用一个综合案例,把前面学习的关于 Dart 的零散知识串起来,希望你可以动手试验一下这个案例,借此掌握如何用 Dart 编程。
有了前面学习的知识点,再加上今天的综合案例练习,我认为你已经掌握了 Dart 最常用的 80% 的特性,可以在基本没有语言障碍的情况下去使用 Flutter 了。至于剩下的那 20% 的特性,因为使用较少,所以我不会在本专栏做重点讲解。如果你对这部分内容感兴趣的话,可以访问官方文档去做进一步了解。
此外,关于 Dart 中的异步和并发,我会在后面的第 23 篇文章“单线程模型怎么保证 UI 运行流畅?”中进行深入介绍。

案例介绍

取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Flutter核心技术与实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(37)

  • relax 置顶
    能不能所有的示例代码放到一个git下面

    作者回复: 代码还是要分开放的,因为每节分享都是独立的一个可运行工程,不只是代码,还有CI和单测一堆工程相关的东西,集中放就没法管理了。
    我单起了一个仓库,用submodule的方式把这些demo都收进去了,可以自取
    https://github.com/cyndibaby905/flutter_core_demo

    2019-08-14
  • Ω
    Item operator+(Item item) => I...
    double get price => bookings...

    这个改造+号的代码还是无法看懂

    作者回复: operator+:把两个Item对象合并为一个。新Item对象的name为这两个对象的name拼接而成,price为他们的price相加而成。

    get price:对列表数据采用累加的方式进行求和。这里用到了reduce方法。reduce是函数迭代方法,需要传递一个二元函数进行列表的合并工作。list[0...n].reduce(f)等于:
    a0 = list[0]
    a1 = f(a0,list[1])
    a2 = f(a1,list[2])
    an = f(an-1,list[n])
    在这里我们的f是求和函数f(x,y)=x+y,可以理解成an=list[0]+list[1]+list[n-1]+list[n]

    2019-07-16
    1
    19
  • bytecode
    double get price() {
      double sum = 0.0;
      for(var i in bookings) {
        sum += i.price;
      }
      return sum;
    }

    文中这里的price()是多了括号(),要去掉()

    作者回复: 感谢提醒

    2019-07-16
    9
  • jia58960
    大概能看懂并实现代码,但构造函数中的属性什么时候用this.属性,什么时候不用this,希望老师能回答下。

    作者回复: 类的实例变量:1.是声明时定义的,可以用this语法糖赋值;2.是继承来的,不能用this,需要把这个值交给super让父类赋值

    2019-07-24
    3
  • 江宁彭于晏
    class Meta {
      double price;
      String name;
      // 成员变量初始化语法糖
      Meta(this.name, this.price);
    }

    class Item extends Meta{
      Item(name, price) : super(name, price);
      // 重载 + 运算符,将商品对象合并为套餐商品
      Item operator+(Item item) => Item(name + item.name, price + item.price);
    }

    class Items {
      Item item;
      double num;
      Items(this.item, this.num);
      double get totalPrice => item.price * num;
    }

    abstract class PrintHelper {
      printInfo() => print(getInfo());
      getInfo();
    }

    //with 表示以非继承的方式复用了另一个类的成员变量及函数
    class ShoppingCart extends Meta with PrintHelper{
      DateTime date;
      String code;
      List<Items> bookings;
      // 以归纳合并方式求和
      double get shopPrice {
         double result = 0.0;
         bookings.forEach((element) => result+=element.totalPrice);
         return result;
      }

      // 默认初始化函数,转发至 withCode 函数
      ShoppingCart({name}) : this.withCode(name:name, code:null);
      //withCode 初始化方法,使用语法糖和初始化列表进行赋值,并调用父类初始化方法
      ShoppingCart.withCode({name, this.code}) : date = DateTime.now(), super(name,0);

      //?? 运算符表示为 code 不为 null,则用原值,否则使用默认值 " 没有 "
      @override
      String getInfo() {
      String bookingsResult = '商品明细: \n';
      bookings.forEach((item) => bookingsResult += ' 商品名称:${item.item.name} 单价:${item.item.price} 数量 :${item.num} 总价:${item.totalPrice}\n');

      return '''
    购物车信息:
    -----------------------------
      用户名: $name
      优惠码: ${code??" 没有 "}
      $bookingsResult
      总价: $shopPrice
      Date: $date
    -----------------------------
    ''';
      }
    }

    void main() {
      ShoppingCart.withCode(name:'张三', code:'123456')
      ..bookings = [Items(Item('苹果',10.0),5.0), Items(Item('鸭梨',20.0),10.0)]
      ..printInfo();

      ShoppingCart(name:'李四')
      ..bookings = [Items(Item('香蕉',15.0),10.0), Items(Item('西瓜',40.0),1.0)]
      ..printInfo();
    }

    作者回复: 赞👍

    不过数量作为Item的属性会更好一点。

    2019-07-19
    3
  • Paradise
    这种结合代码的方式很友好,可以一边听一边上手,更容易理解...😀
    2019-07-16
    3
  • Carlo
    看到一段代码:
    firebaseService.login().then(
                            (value) {
                              print(value.toJson());
                            },
                            onError: (err) {
                              print(err);
                            },
                          );

    请问这个onError是怎么回事啊。为什么会被执行呢? 这是dart特有的语法么?
    我所知道的一般sync function的语法是

    thisFunction()
        .catch(console.error)
        .then(() => console.log('We do cleanup here'));

    作者回复: 给你展开一下你就明白了:
    funcThatThrows()
      .then(successCallback, onError: (e) {
        handleError(e); // Original error.
        anotherFuncThatThrows(); // new error.
      })
      .catchError(handleError); // Error from within then() handled.

    2019-10-15
    1
  • Uncle.Wang
    class Meta {
      double price;
      String name;
      // 成员变量初始化语法糖
      Meta(this.name, this.price);
    }

    abstract class PrintHelper {
      printInfo() => print(getInfo());
      getInfo();
    }

    class Item extends Meta with PrintHelper {
      int count;
      Item(name, price, {int count = 1}): super(name, price) {
        this.count = count;
      }
      // 重载 + 运算符,将商品对象合并为套餐商品
      Item operator+(Item item) => Item(name + item.name, price + item.price);
        @override
      getInfo() => '''
        商品名: $name
        单价: $price
        数量: $count
        ---------------
    ''';
    }

    //with 表示以非继承的方式复用了另一个类的成员变量及函数
    class ShoppingCart extends Meta with PrintHelper{
      DateTime date;
      String code;
      List<Item> bookings;
      // 以归纳合并方式求和
      double get price => bookings.reduce((value, element) => value + element).price;
      // 默认初始化函数,转发至 withCode 函数
      ShoppingCart({name}) : this.withCode(name:name, code:null);
      //withCode 初始化方法,使用语法糖和初始化列表进行赋值,并调用父类初始化方法
      ShoppingCart.withCode({name, this.code}) : date = DateTime.now(), super(name,0);

      getBookingInfo() {
        String str = "";
        for (Item item in bookings) {
          str += item.getInfo();
        }
        return str;
      }

      //?? 运算符表示为 code 不为 null,则用原值,否则使用默认值 " 没有 "
      @override
      getInfo() => '''
    购物车信息:
    -----------------------------
      用户名: $name
      优惠码: ${code??" 没有 "}
      总价: $price
      Date: $date
      商品列表:
        ---------------
    ${ getBookingInfo() }
    -----------------------------
    ''';
    }

    void main() {
      ShoppingCart.withCode(name:'张三', code:'123456')
      ..bookings = [Item('苹果',10.0, count: 10), Item('鸭梨',20.0, count: 5)]
      ..printInfo();

      ShoppingCart(name:'李四')
      ..bookings = [Item('香蕉',15.0), Item('西瓜',40.0)]
      ..printInfo();
    }

    作者回复: 非常棒!

    2019-08-20
    1
  • 和小胖
    老师您的第二个题目,我理解是要做每一种商品的分类,但是我还是用了以前语言的思路来做了。麻烦老师,看看是否正确。

    关键代码如下:
    //定义购物车类
    class ShoppingCar extends Meta with PrintHelper {
      DateTime date; //购物日期
      String code; //优惠券
      List<Item> _bookings; //定义私有属性
      num count; //商品数量

      //求出总价
      double get price =>
          books.reduce((value, element) => value + element).price;

      List<Item> get books => _bookings;

      //给bookings定义set方法
      set book(List<Item> bookings) {
        this._bookings = bookings;
        this.count = bookings.length;
      }

      //获取分类产品信息
      String getGoodsInfo() {
        StringBuffer temp = new StringBuffer();
        Map map = new Map<String, List<Item>>();
        books.forEach((item) {
          if (!map.containsKey(item.name)) {
            List list = new List<Item>();
            list.add(item);
            map[item.name] = list;
          } else {
            (map[item.name] as List<Item>).add(item);
          }
        });
        map.forEach((k, v) {
          temp.write("商品名称:$k,该种商品数量:${(v as List).length},该种商品单价:${(v as List<Item>)[0].price}元\n");
          print("$k,$v");
        });
        return temp.toString();
      }

      //这种表达式赋值还是挺方便的
      ShoppingCar.withCode({name, this.code}): this.date = DateTime.now(),super(name, 0.0);

      ShoppingCar({name}) : this.withCode(name: name, code: null);

      @override
      getInfo() {
        return '''
          购物车信息:
          ----------------------
          用户名:$name
          优惠码:${code ?? "没有优惠券啊"}
          商品总量:$count
          总价:$price
          日期:$date
          ${getGoodsInfo()}
          ----------------------
      ''';
      }
    }

    运行后信息如下:购物车信息:
          ----------------------
          用户名:小李
          优惠码:没有优惠券啊
          商品总量:3
          总价:29.0
          日期:2019-08-14 22:01:24.437085
          商品名称:苹果,该种商品数量:2,该种商品单价:10.0元
          商品名称:梨,该种商品数量:1,该种商品单价:9.0元
          ----------------------

    作者回复: 结果倒是对,但是这个购物车与我们理解的购物车还是有差异:
    1.count属性应该是Item对象的:一个Item对象有count个,而不是一直往购物车内加同样的对象count次;
    2.由于你的count属性无法拆分到Item类,导致getGoodsInfo的计算异常繁琐了。如果count属性在Item类,getGoodsInfo计算可以收敛到Item对象类内部。

    2019-08-14
    1
  • 刘荣清
    在vs code已经安装dart、code runner插件,本地也安装完dart SDK,然后通过vs code运行dart 提示:dart : 无法将“dart”项识别为 cmdlet、函数...
    但在cmd 控制板中就可以正常运行,这是为咋啊,vs code需要额外配置咋吗

    作者回复: 把dart命令所在的目录设置成环境变量PATH

    2019-08-03
    1
  • 灰灰
    abstract class PrintHelper {
      printInfo() => print(getInfo());
      getInfo();
    }

    class Meta {
      double price;
      String name;
      Meta(this.name, this.price);
    }

    // 定义商品Item类
    class Item extends Meta {
      int count;
      Item(name, price, this.count): super(name, price);
      Item operator+(Item item) => Item(name + item.name, price * count + (item.price * item.count), 1);
    }

    //定义购物车类
    class ShoppingCart extends Meta with PrintHelper {
      DateTime date;
      String code;
      List<Item> bookings;

      double get price => bookings.reduce((value, element) => (value + element)).price;
    // ShoppingCart(name, this.code): date = DateTime.now(), super(name,0);
      ShoppingCart({name}): this.withCode(name: name, code: null);
      ShoppingCart.withCode({name, this.code}): date = DateTime.now(), super(name, 0);
      getInfo () => '''
         购物车信息:
         
         ----------------
         商品名 单价 数量 总价
         ----------------
         ${bookings.map((item) => '${item.name} ${item.price} ${item.count} ${item.price * item.count}').join('\n ')}
         
         ================
         用户名: $name
         优惠码: ${code ?? "没有"}
         总价: $price
         日期: $date
      ''';
    }

    void main() {
      ShoppingCart.withCode(name: '张三', code: '123456')
      ..bookings = [Item('苹果', 10.0, 3), Item('鸭梨', 20.0, 1)]
      ..printInfo();

      ShoppingCart(name: '李四')
      ..bookings = [Item('香蕉', 2.4, 2), Item('芒果', 6.8, 4)]
      ..printInfo();
    }

    作者回复: 赞

    2019-07-27
    1
  • DERUWE
    使用了这个Item operator+(Item item) => Item(name + item.name, price + item.price);最后的bookings就变为长度为1的吗
    2019-12-09
  • 信仰年轻
    操作符重载,在kotlin c++里面都有的,用法跟dart一模一样啊
    2019-11-22
  • icer
    class Meta {
      double price;
      String name;
      Meta(this.name,this.price);
    }

    class Item2 extends Meta {
      int num;
      Item2(String name, double price,{this.num=1}) :super(name, price);
      Item2 operator + (Item2 item) => Item2(name + item.name,price * num + item.price * item.num);
    }

    abstract class printHelper {
      printInfo() => print(getInfo());
      getInfo();
    }

    class ShoppingCart2 extends Meta with printHelper {
      DateTime date;
      String code;
      List<Item2> bookings;

      ShoppingCart2({name}) : this.withCode(name:name,code:null);
      ShoppingCart2.withCode({name,this.code}) : date = DateTime.now(),super(name, 0);

      double get price => bookings.reduce((value,element) => value + element).price;

      String get cardInfo {
        String str = "";
        bookings.forEach((v) =>
        str +='${v.name} ----- ${v.num}\n');
        return str;
      }

      getInfo() {
        return
              """
            购物车信息:
            -----------------------------
            用户名: $name
            优惠码: ${code??"没有"}
            $cardInfo
            总价: $price
            日期: $date
            -----------------------------
            """;
      }

    作者回复: 赞👍

    2019-11-19
  • 一个工匠
    666
    2019-11-12
  • 入夜、渐微凉
    在一个道友的回答上稍加改动,加了一个商品总数和购物车为空的情况,老师帮忙看看有没有可以优化的地方。
    class Meta {
      double price;
      String name;
      String info;
      //成员变量初始化语法糖
      Meta(this.name, this.price);
    }
    class Item extends Meta {
      int count;
      Item(name, price, {int count = 1}) : super(name, price) {
        this.count = count;
        this.info = '''
     商品名: $name
       单价: $price
       数量: $count
       ---------------
      ''';
      }
      
      Item operator +(Item item) =>
          Item(name + item.name, price + item.price, count: count + item.count);
    }

    abstract class PrintHelper {
      printInfo() => print(getInfo());
      getInfo();
    }

    //with表示以非继承的方式复用了另一个类的成员变量及函数
    class ShoppingCart extends Meta with PrintHelper {
      DateTime date;
      String code;
      List<Item> bookings;
      //以归纳合并方式求和
      double get price =>bookings != null ?
          bookings.reduce((value, element) => value + element).price : 0.00;
      int get count => bookings != null ? bookings.reduce((value, element) => value + element).count : 0;
      //默认初始化函数,转发至withCode函数
      ShoppingCart({name, List<Item> bookings})
          : this.withCode(name: name, code: null, bookings: bookings);
      //withCode初始化方法,使用语法糖和初始化列表进行赋值,并调用父类初始化方法
      ShoppingCart.withCode({name, this.code, this.bookings})
          : date = DateTime.now(),
            super(name, 0) {
            if (bookings != null) {
                String str = "";
                for (Item item in bookings) {
                  str += item.info;
                }
                info = str;
            }
      }

      //??运算符表示为code不为null,则用原值,否则使用默认值"没有"
      @override
      getInfo() => '''
    购物车信息:
    -----------------------------
      用户名: $name
      优惠码: ${code ?? "没有"}
      商品总数: $count
      总价: $price
      Date: $date
      商品列表:
      ${info ?? "购物车为空"}
    -----------------------------
    ''';
    }

    void main() {
      ShoppingCart.withCode(
          name: '张三',
          code: '123456',
          bookings: [Item('苹果', 10.0, count: 1), Item('鸭梨', 20.0, count: 2)])
        ..printInfo();

      ShoppingCart(
          name: '李四', bookings: [Item('香蕉', 15.0), Item('西瓜', 40.0, count: 4)])
        ..printInfo();

      ShoppingCart(name: '王五')..printInfo();
    }

    作者回复: 计算价格的时候,商品单价要乘以数量;商品合并的时候,数量要置为1

    2019-11-07
  • 狗子不要喝奶茶
    如果方便的话 能否附上课后问题的 解法。方便对一下~自己的思路

    作者回复: 会有的

    2019-10-27
  • 菜头
    多构造函数?
    2019-10-15
  • JW
    老师好!我运行代码出现这个错误,是环境的问题吗?

    Unhandled exception:
    NoSuchMethodError: No static method 'withCode' declared in class 'ShoppingCart'.
    Receiver: ShoppingCart
    Tried calling: withCode(name: "张三", code: "123456")
    #0 NoSuchMethodError._throwNew (dart:core-patch/dart:core/errors_patch.dart:203)
    #1 main (file:///Users/jw/flutter_app/lib/dart_sample.dart:55:16)
    #2 _startIsolate.<anonymous closure> (dart:isolate-patch/dart:isolate/isolate_patch.dart:279)
    #3 _RawReceivePortImpl._handleMessage (dart:isolate-patch/dart:isolate/isolate_patch.dart:165)

    作者回复: 查下你的构造方法声明

    2019-09-22
  • Lihyper
    交作业了,老师

    main() {
      ShopCart.withCode("张三", "123456")
        ..bookings = [Item("苹果", 10.0, 3), Item("樱桃", 100.0, 1)]
        ..printInfo();
    }

    class Meta {
      double price;
      String name;
      int count;

      Meta(this.name, this.price, this.count);
    }

    class Item extends Meta {
      Item(name, price, count) : super(name, price, count);

      Item operator +(Item other) => Item(name + other.name,
          price * count + other.price * other.count, count + other.count);
    }

    class ShopCart extends Meta with PrintHelper {
      String code;
      List<Item> bookings;
      DateTime date;

      // 没有重载,所以出现了可选参数
      //ShopCart(String name) : this.withCode(name,null);

      ShopCart(String name, String code) : this.withCode(name, code);

      ShopCart.withCode(String name, this.code)
          : date = DateTime.now(),
            super(name, 0, 0);

      get price {
        //a??b运算符a!=null ? a:b;
        if (bookings == null || bookings.isEmpty) {
          return 0;
        }
        return bookings.reduce((value, element) => value + element).price;
      }

      get count {
        if (bookings == null || bookings.isEmpty) {
          return 0;
        }
        return bookings.reduce((value, element) => value + element).count;
      }

      getListInfo() {
        String bookInfo = "";
        if (bookings == null || bookings.isEmpty) {
          return bookInfo;
        }
        bookings.forEach((item) =>
            bookInfo += "商品名称:${item.name} 单价:${item.price} 数量:${item.count}\n");
        return bookInfo;
      }

      getInfo() => '''
      购物车信息:
      ---------------------
      用户名: $name
      优惠码: ${code ?? "没有"}
      商品明细:\n ${getListInfo()}
      总价: $price
      总数量: $count
      Date: $date
      ---------------------
      
      ''';
    }

    abstract class PrintHelper {
      printInfo() => print(getInfo());

      getInfo();
    }

    作者回复: 有两个问题:
    1.商品合并后,count需要置为1,而不能做累加。比如五斤苹果和三盒巧克力做合并,结果是一份巧克力苹果套餐,而不是8份巧克力苹果套餐。
    2.为什么ShopCart的两个初始化方法提供的参数一模一样呢?

    2019-08-30
收起评论
37
返回
顶部