flutter学习记录第一节--搭建项目及路由的设置
1.轮播图: flutter_swiper
1.1 用处
可快速实现轮播图的效果
网址:flutter_swipe
1.2 导入flutter_swiper库
在pubspec.yaml 中的dependencies: 添加 ** flutter_swipe: ^1.0.1 ** 这个引用,并保存
1.3 导入库,运行后可能遇到的问题
当引入三方库,运行报如下错误时:Cannot run with sound null safety, because the following dependencies, 参考这个解决方案:
[^1]: 解决方案
即,在终端运行下面的代码:flutter run --no-sound-null-safety
如果按照上述方案,后续的热更,就用命令行看实时的效果
命令行的指令有以下几种:
Flutter run key commands.
r Hot reload. 🔥🔥🔥 (热更,代码修改完后,保存,在控制台输入“r”, 就可以及时看到修改的效果)
R Hot restart. (热启动,代码修改完后,保存,在控制台输入“R”, 就可以重新启动app)
h List all available interactive commands.
d Detach (terminate “flutter run” but leave application running).
c Clear the screen
q Quit (terminate the application on the device).
1.4 属性说明
1.5 代码案例
// 轮播图, 把轮播图组件定义成一个方法,抽离出来
Widget _swiperWidget() {
// 定义一个网络图片的数组
List<Map> imageList = [
{"url": "https://lmg.jj20.com/up/allimg/tp09/210H51R3313N3-0-lp.jpg"},
{"url": "https://lmg.jj20.com/up/allimg/tp07/31041019268143-lp.jpg"},
{"url": "https://lmg.jj20.com/up/allimg/711/121513114600/131215114600-0-1200.jpg"}
];
// 返回一个内容视图
return Container(
// 用AspectRatio 来设置 轮播图的宽高比
child: AspectRatio(
aspectRatio: 2 / 1,
// 用Swiper组件创建轮播图
child: Swiper(
// 每个轮播图的创建方法
itemBuilder: (context, index) {
return Image.network(imageList[index]["url"], fit: BoxFit.fill);
},
// 设置轮播图的个数
itemCount: imageList.length,
// 是否自动播放
autoplay: true,
//
pagination: const SwiperPagination(),
// control: SwiperControl(),
)),
);
}
2. flutter_screenutil
2.1 用处
可以根据设计稿的宽高,自适应所有的设备。
2.2 导入flutter_screenutil库
在pubspec.yaml 中的dependencies: 添加 ·flutter_screenutil: ^5.0.0+2 这个引用,并保存
参考文档: https://blog.csdn.net/shulianghan/article/details/120043532
使用注意事项:需要在使用之前找一个合适的地方,例如在项目第一个页面初始化一下设计稿的宽高
需要用ScreenUtilInit()包装一下
2.3 使用说明
设置宽度:ScreenUtil().setWidth(xx)
设置高度:ScreenUtil().setHeight(x’x)
根据屏幕宽度适配尺寸 : ScreenUtil().setWidth(540) (sdk>=2.6 : 540.w)
根据屏幕高度适配尺寸(一般根据宽度适配即可) :ScreenUtil().setHeight(200) (sdk>=2.6 : 200.h)
根据宽度或高度中的较小者进行适配:ScreenUtil().radius(200) (sdk>=2.6 : 200.r)
适配字体 : ScreenUtil().setSp(24)
取 24和 24.sp 中的最小值: (sdk>=2.6 : 24.sp) 24.sm
设备的像素密度: ScreenUtil.pixelRatio
设备宽度: ScreenUtil.screenWidth (sdk>=2.6 : 1.sw)
设备高度: ScreenUtil.screenHeight (sdk>=2.6 : 1.sh)
底部安全区距离,适用于全面屏下面有按键的: ScreenUtil.bottomBarHeight
状态栏高度 刘海屏会更高: ScreenUtil.statusBarHeight
系统字体缩放比例: ScreenUtil.textScaleFactor
实际宽度与设计稿宽度的比例: ScreenUtil().scaleWidth
实际高度与设计稿高度的比例: ScreenUtil().scaleHeight
屏幕方向: ScreenUtil().orientation
屏幕宽度的 0.2倍: 0.2.sw
屏幕高度的 0.5倍: 0.5.sh
2.4 代码实现按理
class HomePage extends StatefulWidget {
const HomePage({super.key});
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Widget build(BuildContext context) {
// 需要用ScreenUtilInit()包装一下
return ScreenUtilInit(
/// 设置设计稿宽高
designSize: const Size(750, 1334),
///
builder: (context, child) {
return ListView(
children: <Widget>[
_swiperWidget(),
SizedBox(height: ScreenUtil().setHeight(10)),
_titleWidget("猜你喜欢"),
SizedBox(height: ScreenUtil().setHeight(10)),
_titleWidget("热门推荐"),
],
);
},
);
}
// 轮播图
Widget _swiperWidget() {
List<Map> imageList = [
{"url": "https://lmg.jj20.com/up/allimg/tp09/210H51R3313N3-0-lp.jpg"},
{"url": "https://lmg.jj20.com/up/allimg/tp07/31041019268143-lp.jpg"},
{"url": "https://lmg.jj20.com/up/allimg/711/121513114600/131215114600-0-1200.jpg"}
];
return Container(
child: AspectRatio(
aspectRatio: 2 / 1,
child: Swiper(
itemBuilder: (context, index) {
return Image.network(imageList[index]["url"], fit: BoxFit.fill);
},
itemCount: imageList.length,
autoplay: true,
pagination: const SwiperPagination(),
// control: SwiperControl(),
)),
);
}
// 猜你喜欢
Widget _titleWidget(title) {
return Container(
height: ScreenUtil().setWidth(32),
margin: EdgeInsets.only(left: ScreenUtil().setWidth(10)),
padding: EdgeInsets.only(left: ScreenUtil().setWidth(10)),
decoration: BoxDecoration(
border: Border(
left: BorderSide(
color: Colors.red,
width: ScreenUtil().setWidth(10))
),
),
child: Text(title, style: const TextStyle(color: Colors.grey)),
);
}
}
2.5 ScreenUtl 的封装
理由:将三方库常用的方法进一步封装, 原因如下:
- 避免三方库修改方法后, 项目中用的地方太多, 修改起来麻烦
- 三方库的方法太长, 使用起来不方便
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class ScreenHelper {
static init(BuildContext context) {
ScreenUtil.init(
context,
designSize: const Size(750, 1334));
}
static setHeight(double value) {
return ScreenUtil().setHeight(value);
}
static setWidth(double value) {
return ScreenUtil().setWidth(value);
}
static double screenHeight() {
return ScreenUtil().screenHeight;
}
static double screentWidth() {
return ScreenUtil().screenWidth;
}
}
3. json_annotation
3.1 用处
用于网络请求后的, 第三方库执行命令行,自动生成 字典转模型, 或者 模型转字符串的代码
3.2 导入json_annotation库
- 在** pubspec.yaml ** 的文件中, 在dependencies 下 添加
json_annotation: ^4.4.0
- 在** pubspec.yaml ** 的文件中, 在dev_dependencies 下添加
build_runner: ^2.1.8
json_serializable: ^6.1.5
3.3 注意事项
如果导入的版本太低, 会报错,具体的错误, 没有保存,无法做错误案例了
3.4 使用说明
- 在创建的模型中, 添加对应的头文件
import 'package:json_annotation/json_annotation.dart';
- 然后在类的前面添加注解
@JsonSerializable()
用于标识需要自动生成字典转模型的代码。- 在类的前面写上 自动生成代码的路径:
part 'FocusModel.g.dart';
说明:part ‘类名.g.dart’
注释:这时候写这一行,是会报错的,因为还没执行命令,生成代码。执行命令后,自动生成的代码会在这个 ‘类名.g.dart’ 这个文件下。- 执行命令行
flutter packages pub run build_runner build
- 查看是否报错,如果没有报错,基本就成功了。
- 自动生成的代码,主要是生成了两个方法:
6.1_$xxxFromJson(Map<String, dynamic> json) =>{}
6.2Map<String, dynamic> _$xxxToJson(FocusModel instance) =>{}
3.5 案例
// 导入json_annotation库
import 'package:json_annotation/json_annotation.dart';
// 提示系统, 生成的代码放在 “FocusModel.g.dart”文件中, 这句必须得加
part 'FocusModel.g.dart';
// 提示系统, 这个类需要自动生成代码,这句必须得加
()
// 自定义的类
class FocusModel {
final String id;
final String title;
final String pic;
final String url;
FocusModel(this.id, this.title, this.pic, this.url);
// 这个方法是 代码生成后,自己手动添加的方法
factory FocusModel.fromJson(Map<String, dynamic> json) {
return _$FocusModelFromJson(json);
}
// 这个方法是 代码生成后,自己手动添加的方法
Map<String, dynamic> toJson() {
return _$FocusModelToJson(this);
}
}
3.6 参考文档
4. Dio
4.1 用处
用于网络请求
4.1 导入Dio库
- 在** pubspec.yaml ** 的文件中, 在dependencies 下 添加
dio: ^4.0.0-nullsafety.0
4.2 简单使用案例
_getSwiperData() async {
var path = "对应的路径";
var result = await Dio().get(path);
print(result);
}
4.3 用charles 抓不到flutter请求的解决方法
原因:在使用Dio发送请求时,如果没有正确配置代理、SSL证书等相关参数,会导致抓包工具无法捕获数据包。
配置如下:
import 'dart:convert';
import 'dart:io';
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
// 自定义一个网络请求的基类
class HttpRequest {
late Dio dio;
// 代理地址和端口号替换为你实际的代理服务器地址和端口号
String proxy = 'PROXY 192.168.2.101:8888'; // ip:port
HttpRequest() {
dio = Dio();
// 配置HTTP代理
var clientAdapter = DefaultHttpClientAdapter();
clientAdapter.onHttpClientCreate = (client) {
client.findProxy = (uri) {
// 此处将代理地址和端口号替换为你实际的代理服务器地址和端口号
return proxy;
};
// 配置证书
client.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);
return client;
};
dio.httpClientAdapter = clientAdapter;
dio.options.headers = {
'Content-Type': 'application/json;charset=UTF-8',
};
}
Future<Map<String, dynamic>> get(String path,
{Map<String, dynamic>? queryParameters}) async {
try {
Response response = await dio.get(path, queryParameters: queryParameters);
return json.decode(response.toString());
} on DioError catch (e) {
if (e.response != null) {
// 请求成功,但是服务器返回错误信息
return json.decode(e.response.toString());
} else {
// 请求失败
throw Exception('网络异常,请稍后重试');
}
}
}
}
//------在需要网络请求的类中使用------
// 获取轮播图的数据
_getSwiperData() async {
var path = "http://zhuzhu.test/home/swiper";// "http://broadcast.join-test.viz-cloud.top:9666/ig/api/app/login";
var result = await HttpRequest().get(path);
print(result);
setState(() {
_focusDataList = FocusAllModel.fromJson(result).result;
});
}
4.4 抓包配置的注意项
在以上代码中,我们设置了HTTP代理地址和端口号,并关闭了SSL证书验证。注意:在生产环境中不应该关闭SSL证书验证,这只是为了方便调试时使用。
记录一下之前设置代理不走的原因:
- 没有正确配置代理
如果在使用Dio时需要通过代理服务器发送请求,那么需要在onHttpClientCreate方法中进行相应的配置。具体来说,需要将client.findProxy方法的返回值设置为代理服务器的地址和端口号。
dio.httpClientAdapter = DefaultHttpClientAdapter()
..onHttpClientCreate = (client) {
// 设置代理地址和端口号
client.findProxy = (uri) {
return "PROXY proxy.example.com:8888";
};
// 必须返回一个HttpClient实例
return client;
};
- 没有正确配置SSL证书验证
如果在使用Dio时需要验证HTTPS请求的SSL证书,那么需要在onHttpClientCreate方法中进行相应的配置。具体来说,需要将client.badCertificateCallback方法设置为一个验证函数。
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(client) {
// 关闭证书验证
client.badCertificateCallback =
((X509Certificate cert, String host, int port) => true);
return client;
};
4.5 如果不知道什么是Charles的请查看下面的教程
…待更新…