在我个人认为学习一门新的言语(快速高效学习) 一定是经过实践,最好的便是做项目,这儿我会简略写一个京东的Demo。
第一天 搭建项目框架,完成首页的功用:/editor/draf…
第二天完成 分类和产品列表页面: /post/704471…
第三天完成 产品详情页功用:/editor/draf…
Flutter-混合工程的持续集成实践: /post/704209…
前面完成了首页、分类页面、产品列表页和产品详情页的功用,这篇文章完成购物车页面的功用。
用到的知识点
1. shared_preferences 完成本地数据存储
shared_preferences
是 Flutter 供给的 key-value 存储插件,能够将数据耐久化到磁盘中,支撑 Android 和 iOS,在 iOS 中是基于 NSUserDefaults
,在 Android 中基于SharedPreferences
。
在项目的 pubspec.yaml
文件中添加依靠:shared_preferences: ^2.0.11
,然后履行 pub get
,
shared_preferences 支撑的数据类型有 int、double、bool、string、stringList。
在services
文件里边界说一个storage.dart
,在里边封装常用的功用:
import 'package:shared_preferences/shared_preferences.dart';
class Storage {
//设置值
static Future<void> setString(key, value) async {
SharedPreferences sp = await SharedPreferences.getInstance();
var result = sp.setString(key, value);
}
//获取值
static Future<String?> getString(key) async{
SharedPreferences sp = await SharedPreferences.getInstance();
var result = sp.getString(key);
return result;
}
//删去值
static Future<void> remove(key) async {
SharedPreferences sp = await SharedPreferences.getInstance();
sp.remove(key);
}
//整理值
static Future<void> clear() async {
SharedPreferences sp = await SharedPreferences.getInstance();
sp.clear();
}
}
我这儿是用了String类型举例,封装了一个类专门办理。在之前的文章中页讲到数据存储的两种方法:/post/704098…
2. JSON 转 Model
在日常开发中JSON的序列化与反序列化
是一个常见的操作,假设都是咱们手动去解析JSON数据,是很费事的。假设能够主动转化就省去了许多工作。
东西完成
在iOS上面我就找到了一个东西能够主动转化 json 数据:/post/702689… 。那在Flutter 中也找到了转化的东西:app.quicktype.io ,相对来讲也是比较好用的。
这样就能够完成转化。由于Flutter禁用运行时反射,才导致没有像iOS成熟的库完成解析,比方 MJExtension
、 YYModel
,这儿介绍一个相对成熟的库 json_serializable 完成转化。
json_serializable 完成
在项目的 pubspec.yaml
文件中添加依靠:
json_serializable: ^6.1.3
build_runner: ^2.1.7
json_annotation: ^4.4.0
然后履行 pub get
。要想运用转化,首先要先用东西生成模型类,东西地址:caijinglong.github.io/json2dart/i…
在项目里边创建模型类,把东西转化的代码拷贝到这个模型类里边
import 'package:json_annotation/json_annotation.dart';
part 'person.g.dart';
List<person> getpersonList(List<dynamic> list){
List<person> result = [];
list.forEach((item){
result.add(person.fromJson(item));
});
return result;
}
@JsonSerializable()
class person extends Object with _$personSerializerMixin{
@JsonKey(name: 'name')
String name;
@JsonKey(name: 'age')
String age;
@JsonKey(name: 'tele')
String tele;
person(this.name,this.age,this.tele,);
factory person.fromJson(Map<String, dynamic> srcJson) => _$personFromJson(srcJson);
}
接下来在终端履行flutter packages pub run build_runner watch
,就会在项目里边生成person.g.dart
文件,这个里边便是转化好的代码。
也能够履行 flutter packages pub run build_runner build
生成 person.g.dart
文件,区别在于上面是持续生成,下面这个是一次性生成。
留意上面东西运用时,会按着list里边第一个map里边的数据进行解析,假设数组里边其他map字段比较多,就会存在漏字段的情况,这个还需要留意检查下。全体运用下来也不是很便利,还不如用东西直接生成简略:app.quicktype.io 。
插件 JsonToDart 完成
zhuanlan.zhihu.com/p/163330265 这个插件也能够完成转化
在 Android Studio 中装置 JsonToDart
插件,翻开 Preferences(Mac)或许 Setting(Window),挑选 Plugins,搜索 JsonToDart
点击 Install 装置,装置完成后重启。这个时分选定目录,点击右键,挑选 New->Json to Dart
,或许运用快捷键
Windows:ALT + Shift + D
Mac:Option + Shift + D
选中 Json To Dart
后,弹出页面输入要转化的json数据
点击完成,就会生成对应的模型文件了
这个是三个json转model计划里边最简略
的了。
上篇文章完成了五种JSON转Model的计划:/post/704701…
3. 在不同分辨率的手机上检查UI作用
Flutter 开发最大的优势便是其跨渠道,当开发完成时,想在不同分辨率的手机检查其作用,假设跑每个机型去看作用还是比较费事的。这个包 device_preview
能够完成检查不同分辨率手机上的UI作用。
装备 device_preview: ^1.0.0
,然后履行 pub get
。在 main.dart里边运用
import 'package:device_preview/device_preview.dart';
void main() => runApp(
DevicePreview(
enabled: !kReleaseMode,//在非release环境下运用
builder: (context) => MyApp(), // Wrap your app
),
);
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
useInheritedMediaQuery: true,
locale: DevicePreview.locale(context),
builder: DevicePreview.appBuilder,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
home: const HomePage(),
);
}
}
这个包能够完成下列功用:
- 更改设备方向
- 动态系统装备:言语,暗形式,文本缩放比例
- 可自在调整分辨率和安全区域的设备
- 保持应用程序状况
- 截图
4. Provider 状况办理
什么是Provider 状况办理?
当咱们想在多个页面(组件/Widget)之间同享状况(数据),或许一个页面(组 件/Widget)中的多个子组件之间同享状况(数据),这个时分咱们就能够用 Flutter 中的状况办理来办理一致的状况(数据),完成不同组件直接的传值和数据同享。provider
是 Flutter 官方团队
推出的状况办理形式。
详细的运用:
- 装备
provider: ^6.0.1
, - 新建一个文件夹叫 provider,在 provider 文件夹里边放咱们关于的状况办理类
- 在 provider 里边新建 cart.dart
- cart.dart 里边新建一个类承继
ChangeNotifier
代码如下,这儿主要是处理购物车中的数据
class Cart with ChangeNotifier {
List _cartList = [];//购物车数据
bool _isCheckAll = false;//全选
double _allPrice = 0;//总价
List get cartList => _cartList;
bool get isCheckAll => _isCheckAll;
double get allPrice => _allPrice;
Cart(){
this.init();
}
//初始化的时分获取购物车数据
init() async {
String? cartList = await Storage.getString(('cartList'));
if(cartList != null){
List cartListData = json.decode(cartList);
_cartList = cartListData;
} else {
_cartList = [];
}
//获取全选的状况
_isCheckAll = this.isCheckAll;
//核算总价
computeAllPrice();
notifyListeners();
}
updateCartList() {
this.init();
}
itemCountChange() {
Storage.setString('cartList', json.encode(_cartList));
//核算总价
computeAllPrice();
notifyListeners();
}
//全选 反选
checkAll(value) {
for (var i = 0; i < _cartList.length; i++) {
_cartList[i]['checked'] = value;
}
_isCheckAll = value;
//核算总价
computeAllPrice();
Storage.setString('cartList', json.encode(_cartList));
notifyListeners();
}
//判断是否全选
bool isCheckedAll() {
if (_cartList.length > 0) {
for (var i = 0; i < cartList.length; i++) {
if (_cartList[i]['checked'] == false) {
return false;
}
}
return true;
}
return false;
}
//监听每一项的选中事情
itemChage() {
if (isCheckAll == true) {
_isCheckAll = true;
} else {
_isCheckAll = false;
}
//核算总价
computeAllPrice();
Storage.setString('cartList', json.encode(_cartList));
notifyListeners();
}
//核算总价
computeAllPrice() {
double tempAllPrice = 0;
for (var i = 0; i < _cartList.length; i++) {
if (_cartList[i]['checked'] == true) {
tempAllPrice += _cartList[i]['price'] * _cartList[i]['count'];
}
}
_allPrice = tempAllPrice;
notifyListeners();
}
//删去数据
removeItem() {
List tempList=[];
for (var i = 0; i < _cartList.length; i++) {
if (_cartList[i]['checked'] == false) {
tempList.add(_cartList[i]);
}
}
_cartList=tempList;
//核算总价
computeAllPrice();
Storage.setString('cartList', json.encode(_cartList));
notifyListeners();
}
}
最终别忘记在main.dart
中的MultiProvider
添加上这个文件
providers:[
ChangeNotifierProvider(create: (_) => CheckOut()),
ChangeNotifierProvider(create: (_) => Cart()),
],
完成作用
详细完成代码
界面框架代码
class CartPage extends StatefulWidget {
CartPage({Key? key}) : super(key: key);
_CartPageState createState() => _CartPageState();
}
class _CartPageState extends State<CartPage> {
bool _isEdit = false;
var checkOutProvider;
@override
void initState() {
super.initState();
}
//去结算
doCheckOut() async {
//1、获取购物车选中的数据
List checkOutData = await CartServices.getCheckOutData();
//2、保存购物车选中的数据
this.checkOutProvider.changeCheckOutListData(checkOutData);
//3、购物车有没有选中的数据
if (checkOutData.length > 0) {
Navigator.pushNamed(context, '/checkOut');
} else {
Fluttertoast.showToast(
msg: '购物车没有选中的数据',
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
);
}
}
@override
Widget build(BuildContext context) {
var cartProvider = Provider.of<Cart>(context);
checkOutProvider = Provider.of<CheckOut>(context);
return Scaffold(
appBar: AppBar(
title: Text('购物车'),
actions: [
IconButton(onPressed: (){
}, icon: Icon(Icons.launch))
],
),
body: cartProvider.cartList.length > 0 ? Stack(
children: [
//列表
ListView(
children: [
Column(
children: [
Column(
children: cartProvider.cartList.map((value){
//返回生成每个Item
return CartItem(value);
}).toList(),
),
SizedBox(height: ScreenAdapter.height(100))
],
)
],
),
//底部的全选和结算按钮
Positioned(
bottom: 0,
width: ScreenAdapter.width(750),
height: ScreenAdapter.height(78),
child: Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(width: 1, color: Colors.black12),
),
color: Colors.white
),
width: ScreenAdapter.width(750),
height: ScreenAdapter.height(78),
child: Stack(
children: [
Align(
alignment: Alignment.centerLeft,
child: Row(
children: [
Container(
width: ScreenAdapter.width(60),
child: Checkbox(
value: false,
activeColor: Colors.pink,
onChanged: (v){
},
),
),
Text('全选'),
],
),
),
Align(
alignment: Alignment.centerRight,
child: Container(
margin: EdgeInsets.only(right: 10),
child: ElevatedButton(
child: Text('结算', style: TextStyle(color: Colors.white),),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.red),
),
onPressed: (){
doCheckOut();
},
),
),
)
],
),
)
),
],
) : Center(
child: Text("购物车空空的..."),
),
);
}
}
每个Item的完成代码
独自创建一个cart文件夹,在里边放在主页面抽离的代码
class CartItem extends StatefulWidget {
Map _itemData;
CartItem(this._itemData,{Key? key}) : super(key: key);
_CartItemState createState() => _CartItemState();
}
class _CartItemState extends State<CartItem> {
//从本地存储的数据里边读取的
late Map _itemData;
@override
Widget build(BuildContext context) {
//留意:给属性赋值
this._itemData=widget._itemData;
//经过Provider完成了页面和组件间的数据同享
var cartProvider = Provider.of<Cart>(context);
return Container(
height: ScreenAdapter.height(220),
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
border: Border(bottom: BorderSide(width: 1, color: Colors.black12))),
child: Row(
children: <Widget>[
Container(
width: ScreenAdapter.width(60),
child: Checkbox(
value: _itemData["checked"],
onChanged: (val) {
_itemData["checked"]=!_itemData["checked"];
//更新数据
cartProvider.itemChage();
},
activeColor: Colors.pink,
),
),
Container(
width: ScreenAdapter.width(160),
child: Image.network(
"${_itemData["pic"]}",
fit: BoxFit.cover),
),
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.fromLTRB(10, 10, 10, 5),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("${_itemData["title"]}",
maxLines: 2),
Text("${_itemData["selectedAttr"]}",
maxLines: 2),
Stack(
children: <Widget>[
Align(
alignment: Alignment.centerLeft,
child: Text("¥${_itemData["price"]}",style: TextStyle(
color: Colors.red
)),
),
Align(
alignment: Alignment.centerRight,
//完成添加/削减物品件数
child: CartNum(_itemData),
)
],
)
],
),
),
)
],
),
);
}
}
每件产品的数量加/减组件代码
class CartNum extends StatefulWidget {
Map _itemData;
CartNum(this._itemData,{Key? key}) : super(key: key);
_CartNumState createState() => _CartNumState();
}
class _CartNumState extends State<CartNum> {
late Map _itemData;
var cartProvider;
@override
Widget build(BuildContext context) {
//留意
_itemData=widget._itemData;
cartProvider = Provider.of<Cart>(context);
return Container(
width: ScreenAdapter.width(168),
decoration:
BoxDecoration(border: Border.all(width: ScreenAdapter.width(2), color: Colors.black12)),
child: Row(
children: <Widget>[
_leftBtn(),
_centerArea(),
_rightBtn()
],
),
);
}
//左边按钮
Widget _leftBtn() {
return InkWell(
onTap: () {
if(_itemData["count"]>1){
_itemData["count"]--;
cartProvider.itemCountChange();
}
},
child: Container(
alignment: Alignment.center,
width: ScreenAdapter.width(45),
height: ScreenAdapter.height(45),
child: Text("-"),
),
);
}
//右侧按钮
Widget _rightBtn() {
return InkWell(
onTap: (){
_itemData["count"]++;
cartProvider.itemCountChange();
},
child: Container(
alignment: Alignment.center,
width: ScreenAdapter.width(45),
height: ScreenAdapter.height(45),
child: Text("+"),
),
);
}
//中间
Widget _centerArea() {
return Container(
alignment: Alignment.center,
width: ScreenAdapter.width(70),
decoration: BoxDecoration(
border: Border(
left: BorderSide(width: ScreenAdapter.width(2), color: Colors.black12),
right: BorderSide(width: ScreenAdapter.width(2), color: Colors.black12),
)),
height: ScreenAdapter.height(45),
child: Text("${_itemData["count"]}"),
);
}
}
provider 代码
经过运用provider完成了数据同享,创建了两个文件 cart.dart 和 check_out.dart
class Cart with ChangeNotifier {
List _cartList = [];//购物车数据
bool _isCheckAll = false;//全选
double _allPrice = 0;//总价
List get cartList => _cartList;
bool get isCheckAll => _isCheckAll;
double get allPrice => _allPrice;
Cart(){
this.init();
}
//初始化的时分获取购物车数据
init() async {
String? cartList = await Storage.getString(('cartList'));
if(cartList != null){
List cartListData = json.decode(cartList);
_cartList = cartListData;
} else {
_cartList = [];
}
//获取全选的状况
_isCheckAll = this.isCheckAll;
//核算总价
computeAllPrice();
notifyListeners();
}
updateCartList() {
this.init();
}
itemCountChange() {
Storage.setString('cartList', json.encode(_cartList));
//核算总价
computeAllPrice();
notifyListeners();
}
//全选 反选
checkAll(value) {
for (var i = 0; i < _cartList.length; i++) {
_cartList[i]['checked'] = value;
}
_isCheckAll = value;
//核算总价
computeAllPrice();
Storage.setString('cartList', json.encode(_cartList));
notifyListeners();
}
//判断是否全选
bool isCheckedAll() {
if (_cartList.length > 0) {
for (var i = 0; i < cartList.length; i++) {
if (_cartList[i]['checked'] == false) {
return false;
}
}
return true;
}
return false;
}
//监听每一项的选中事情
itemChage() {
if (isCheckAll == true) {
_isCheckAll = true;
} else {
_isCheckAll = false;
}
//核算总价
computeAllPrice();
Storage.setString('cartList', json.encode(_cartList));
notifyListeners();
}
//核算总价
computeAllPrice() {
double tempAllPrice = 0;
for (var i = 0; i < _cartList.length; i++) {
if (_cartList[i]['checked'] == true) {
tempAllPrice += _cartList[i]['price'] * _cartList[i]['count'];
}
}
_allPrice = tempAllPrice;
notifyListeners();
}
//删去数据
removeItem() {
List tempList=[];
for (var i = 0; i < _cartList.length; i++) {
if (_cartList[i]['checked'] == false) {
tempList.add(_cartList[i]);
}
}
_cartList=tempList;
//核算总价
computeAllPrice();
Storage.setString('cartList', json.encode(_cartList));
notifyListeners();
}
}
class CheckOut with ChangeNotifier {
List _checkOutListData = []; //购物车数据
List get checkOutListData => _checkOutListData;
changeCheckOutListData(data){
_checkOutListData=data;
notifyListeners();
}
}
以上便是购物车页面的完成代码。