Flutter 核心技术与实战
陈航
前美团点评高级技术专家
42432 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 48 讲
Flutter 核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

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

Mixin
??运算符
命名参数
字符串插值
箭头函数简化
重载运算符
抽象类Meta
ShoppingCart类初始化简化
Item类初始化简化
对象初始化方式的优化
方法改造
类抽象改造
main函数
ShoppingCart类
Item类
扩展购物车程序功能
思考题
代码示例
Dart设计思想的程序改造
Dart语法特性改造
初始版本
Dart购物车程序
第三篇文章:综合案例,串起Dart的零散知识
前两篇文章:Dart程序的基本结构和语法
作者:陈航
思考题
总结
案例介绍
介绍
综合案例:掌握Dart核心特性

该思维导图由 AI 生成,仅供参考

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

案例介绍

确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文以Dart语言编写购物车程序为例,逐步介绍了如何利用Dart语言特性优化程序结构和实现。作者首先展示了一个简单的购物车程序,然后通过类抽象改造和方法改造,逐步优化了程序的结构和实现。在类抽象改造中,作者利用语法糖和初始化列表简化了构造函数的赋值过程,并抽象出一个基类Meta,使程序中各个类的依赖关系更加清晰。在方法改造中,作者重载了"+”运算符,实现了商品的归纳合并,同时使用箭头函数和多行字符串声明来简化代码,使程序更加简洁和优雅。通过这些改造,读者可以深入了解Dart语言的核心特性,包括语法糖、初始化列表、重载运算符等,以及如何利用这些特性优化程序结构和实现。文章还介绍了如何使用Mixin实现类的复用,以及级联操作符“..”简化代码。总之,本文通过实例展示了Dart语言的灵活性和强大特性,对于想要深入学习Dart语言的读者来说,是一份很好的综合案例,能够帮助他们更好地掌握Dart编程的技巧和思想。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Flutter 核心技术与实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(68)

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

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

    2019-08-14
    3
    4
  • Ω
    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
    3
    35
  • bytecode
    double get price() { double sum = 0.0; for(var i in bookings) { sum += i.price; } return sum; } 文中这里的price()是多了括号(),要去掉()

    作者回复: 感谢提醒

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

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

    2019-07-24
    2
    10
  • 灰灰
    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
    2
    5
  • 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
    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
  • 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
  • 和小胖
    老师您的第二个题目,我理解是要做每一种商品的分类,但是我还是用了以前语言的思路来做了。麻烦老师,看看是否正确。 关键代码如下: //定义购物车类 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
收起评论
显示
设置
留言
68
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部