// 简单使用
import 'package:dio/dio.dart';
void main() async {
var dio = Dio();
final response = await dio.get('https://google.com');
print(response.data);
}
// response.data 这里返回的是json字符串,可使用 data['name'] 获取值
// 要想像 Object 一样使用 data.name 获取。需要转换为Dart模型(Json to Dart)
在pub.dev的插件市场中,瀑布流布局的插件种类繁多,让人看得眼花缭乱。面对这么多选项,如何挑选出适合自己项目的插件变得尤为重要。这其中的每一个细节都值得仔细考虑。这情形就好像在众多珍宝中寻找最耀眼、最适合自己的那一颗。
{
"name": "jsdawn",
"age": 18,
"gender": ""
}
// To parse this JSON data, do
// final person = personFromJson(jsonString);
import 'dart:convert';
// 字符串转为`Person`模型
Person personFromJson(String str) => Person.fromJson(json.decode(str));
// `Person`数据转为字符串
String personToJson(Person data) => json.encode(data.toJson());
class Person {
Person({
this.name,
this.age,
this.gender,
});
String name;
int age;
String gender;
// json中字段不可少,否则需要判空:json["name"] ?? ""
factory Person.fromJson(Map<String, dynamic> json) => Person(
name: json["name"],
age: json["age"],
gender: json["gender"],
);
Map<String, dynamic> toJson() => {
"name": name,
"age": age,
"gender": gender,
};
}
考虑项目开发环境
// 这里 DefaultAssetBundle 的 loadString 加载 Json 文件,模拟接口调用
String jsonString = await DefaultAssetBundle.of(context)
.loadString('assets/json/data.json');
// 字符串转为 Dart Model
final person = personFromJson(jsonString);
print(person.name); // jsdawn
assets:
- assets/svg/
项目使用的是v2.x版本的开发环境,这个版本能够兼容Null。因此,选择的瀑布流布局插件也必须能够适配Null。根据以往的项目经验,若插件不兼容开发环境的特性,后续可能会遇到众多兼容性问题。在开发过程中,这就像建造房屋时基础不稳,后续楼层建设将充满挑战。此外,不同版本对插件的要求各异,忽视这些要求可能导致项目架构不稳定。在实际操作中,若插件不支持Null,模块间或数据间的交互可能会出现意料之外的错误。
import 'package:flutter_svg/svg.dart';
Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
// SvgPicture.asset 加载svg静态资源
child: SvgPicture.asset('assets/svg/category_phone.svg', height: 35),
),
项目环境中存在诸多特性,各团队可能关注点各异。不过,对于本项目而言,v2.x版本对Null的支持显得尤为关键。
参考插件的LIKES数量
满足开发需求之外,插件获得点赞数同样关键。众多开发者点赞,表明该插件在功能与稳定性上有所长处。有项目曾随意选用点赞稀少的插件,结果使用时故障频发。选用点赞数较高的插件,能降低这种风险。在插件市场,人们的选择通常映射出插件的真实品质。数据显示,点赞数多的插件用户满意度通常也较高,这些都是开发实践中能直观感受到的。
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
@override
Widget build(BuildContext context) {
// 使用插件的 MasonryGridView 类编写瀑布流布局
return MasonryGridView.count(
controller: _scrollController,
padding: widget.list.isNotEmpty
? const EdgeInsets.symmetric(vertical: 12)
: null,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 2, // 列的数量
mainAxisSpacing: 20, // 主轴项之间的距离
crossAxisSpacing: 12, // 交叉轴项之间的距离
itemCount: widget.list.length,
// 列表项生成器
itemBuilder: (context, index) {
return GoodsListItem(widget.list[index]);
},
);
}
当然,这并非衡量事物的唯一准则,然而,它确实是一个至关重要的考量因素。因为,公众的视角在一定程度上,确实具有一定的参考意义。
瀑布流布局需求匹配度
项目需求插件需适配瀑布流布局。以商品列表组件为例,页面布局需呈现良好视觉感受。我曾观察过一个项目,选用的插件看似满足瀑布流布局标准,但处理商品图片时却出现故障。在现实应用中,商品列表的呈现可能左右用户购买意愿。若商品图片拉伸扭曲或间距不均,用户体验将大打折扣。因此,在选择瀑布流布局插件时,务必细致检查其是否完全符合项目布局要求,这一点至关重要。
{
"id": 46,
"title": "能方研影",
"price": 61.96,
"cover": "http://dummyimage.com/300x300/B9C6C3&text=shopping%20mall",
"count": 3,
"color": "蓝色",
"size": "XS",
}
另外,还需注意不同商品在陈列上各有不同需求,有的商品适合用多张图片来展示,而有的则以文字描述为主,这些因素都需纳入考量范围。
class CartInfoModel {
CartInfoModel({
required this.id,
required this.title,
required this.price,
required this.cover,
required this.count,
required this.color,
required this.size,
});
int id;
String title;
double price;
String cover;
int count;
String color;
String size;
...
}
本地存储相关事项
购物车商品的本地保存对于项目来说至关重要。当用户没有结账就关闭应用,期待下次打开时购物车商品还在,这直接影响到用户的体验。以我们平时用的购物应用为例,若关闭后再打开发现购物车为空,难免会感到沮丧。在实现本地保存时,可以借助特定的插件实现数据的长期保存。在具体的项目中,通过执行pubadd指令来下载这些插件,这是基本步骤。同时,当购物车里的数据发生变化,比如商品数量的增减等,要确保及时更新到本地存储,这样才能确保下次打开应用时购物车数据准确无误。
import 'package:flutter_riverpod/flutter_riverpod.dart';
...
const cartPrefsKey = 'cartList';
class CartNotifier extends ChangeNotifier {
// 购物车列表
final List<CartInfoModel> cartList = <CartInfoModel>[];
// 总数量
int get totalCount {
int total = 0;
for (var item in cartList) {
total += item.count;
}
return total;
}
// 总价格
double get totalPrice {
double total = 0;
for (var item in cartList) {
total += item.price * item.count;
}
return total;
}
// 加入购物车
// 更新购物车数量
// 移除购物车指定项
// 查询/初始化购物车
}
// ChangeNotifierProvider 用法
final cartProvider = ChangeNotifierProvider<CartNotifier>((ref) {
return CartNotifier();
});
本地存储可能存在数据安全的隐患,但在此项目里并未涉及。显然,若涉及用户隐私等关键信息,安全因素同样十分关键。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(
// ProviderScope包裹入口组件,提供全局状态
ProviderScope(child: MyApp())
);
}
全局状态管理
// 读取购物车列表
List<CartInfoModel> list =
ref.watch(cartProvider.select((value) => value.cartList));
// 读取结算价格
double totalPrice =
ref.watch(cartProvider.select((value) => value.totalPrice));
// 读取总数量
int totalCount =
ref.watch(cartProvider.select((value) => value.totalCount));
// 调用方法
ref.read(cartProvider.notifier).pushCart(_cartInfo);
购物车全局状态管理非常重要。它需在多个页面间共享,比如在商品详情页展示购物车商品数,并允许直接添加商品;在购物车页面上还能调整商品数量。这相当于一条线将所有购物车操作串联起来。采用官方推荐的响应式缓存和绑定框架管理购物车状态,能实现数据跨组件共享。每次操作购物车,都要同步更新状态。若处理不当,可能导致购物车数据显示错误等问题。
举个例子,过去有些类似的项目在数据状态管理上出了问题。当在两个页面同时操作购物车时,商品数量就变得混乱无序。
遇到空安全错误的解决
/// 加入购物车
void pushCart(CartInfoModel cartInfo) {
// 查找 id/color/size 都相同的购物车商品
int idx = cartList.indexWhere((item) => (item.id == cartInfo.id &&
item.color == cartInfo.color &&
item.size == cartInfo.size));
if (idx > -1) {
// 存在则数量+1
cartList[idx].count += cartInfo.count;
} else {
// 不存在则添加(这里深拷贝cartInfo)
cartList.add(cartInfoModelFromJson(cartInfoModelToJson(cartInfo)));
}
// 通知 CartNotifier 状态更新
notifyListeners();
// 更新数据存储到本地
updCartPrefs();
}
/// 更新购物车数量
void updCartCount(CartInfoModel cartInfo, int count) {
// 查找 id/color/size 都相同的购物车商品
int idx = cartList.indexWhere((item) => (item.id == cartInfo.id &&
item.color == cartInfo.color &&
item.size == cartInfo.size));
if (idx > -1) {
// 存在则更新数量
cartList[idx].count = count;
}
notifyListeners();
// 更新数据存储到本地
updCartPrefs();
}
/// 移除购物车指定商品
void renmoveCart(CartInfoModel cartInfo) {
// 查找 id/color/size 都相同的购物车商品
int idx = cartList.indexWhere((item) => (item.id == cartInfo.id &&
item.color == cartInfo.color &&
item.size == cartInfo.size));
if (idx > -1) {
// 存在则移除该项
cartList.removeAt(idx);
}
notifyListeners();
// 更新数据存储到本地
updCartPrefs();
}
项目开发中,若采用v2版或以上,运行或打包时若出现空安全错误,通常是因为某些编写方式或插件未遵循空安全标准。这时,推荐更换支持空安全的插件。就像机器某个部件不匹配,无法运作,需找到合适的部件才能让机器正常运转。在开发过程中,若此类空安全错误未能及时处理,会延误项目进度。而且,这些错误可能潜藏在代码的各个部分,不易察觉。因此,对开发版本的特性需时刻保持警觉,并严格按照要求挑选插件。
在项目开发过程中,大家是否曾面临过挑选插件时的难题或是遇到空安全错误的问题?期待大家踊跃留言交流。若觉得这篇文章对您有帮助,不妨点赞并转发。
import 'package:shared_preferences/shared_preferences.dart';
class CartNotifier extends ChangeNotifier {
final List<CartInfoModel> cartList = <CartInfoModel>[];
...
/// 更新购物车数据到本地持久化存储
void updCartPrefs() async {
final prefs = await SharedPreferences.getInstance();
// 购物车数据转为字符串
String cartString = json.encode(cartList).toString();
// `setString`存储到本地
prefs.setString('cartList', cartString);
}
/// 查询/初始化 - 从本地读取数据恢复到购物车状态
Future<List<CartInfoModel>> getCartList() async {
// 获取持久化实例
final prefs = await SharedPreferences.getInstance();
// 调用`getString`方法读取本地字符串数据
String? _cartString = prefs.getString('cartList');
cartList.clear();
// 字符串数据转换为`CartInfoModel`初始化到购物车列表
if (_cartString != null) {
List<CartInfoModel> _list = cartInfoModelListFromJson(_cartString);
cartList.addAll(_list);
}
notifyListeners();
return cartList;
}
}