1.本地图片
-
需要在和lib平级的assets目录下建立相应的目录,分为1倍,2倍,3倍三个不同的区域
-
图片资源选Android的,分为mdpi(1倍),xhdpi(2倍),xxhdpi(3倍)三种;下载之后重命名成相同的文件名(包括扩展名),然后分别放到不同的文件夹。
然后用Android Studio的插件Flutter img sync在R文件中生产一下,就会在pubspec.yaml文件中完成注册,并且把字符串转化为全局变量。
在使用的地方采用组件Image.asset()进行显示
child: Image.asset(
R.assetsImgAppleLogin,
width: 42.w,
height: 42.w,
fit: BoxFit.cover,
),
其中资源全局变量的内容为static final String assetsImgAppleLogin = 'assets/img/apple_login.png';
2. 网络图片
Flutter系统提供的Image.network()可以用,但是很不好用,一般不会用它
-
一般都会用插件cached_network_image来显示网络图片,点赞数很高
-
这个图片插件会吃尽Flutter的内存,导致闪退,所以它推荐了配合使用的缓存插件flutter_cache_manager点赞数也是较高的。
由于用的地方很多,所以最好封装成一个组件,方便使用。并且url不合法的时候,会产生异常,导致页面白屏,所以在组件中增加对url合法性判断。
class NetworkImageWidget extends StatelessWidget {
final String? url;
final String? placeholder;
final double? width;
final double? height;
final BoxFit fit;
const NetworkImageWidget({
super.key,
this.url,
this.width,
this.height,
this.placeholder,
this.fit = BoxFit.cover,
});
@override
Widget build(BuildContext context) {
String actualUrl;
bool isUrlExist;
String actualPlaceholder;
/// 判断url是否存在
if ((url != null) && url!.isNotEmpty && (url!.length > 6)) {
isUrlExist = true;
actualUrl = url!;
} else {
isUrlExist = false;
actualUrl = '';
}
/// url有可能以//开头,缺少https
if (isUrlExist) {
if (!actualUrl.startsWith('http')) {
actualUrl = 'https:$actualUrl';
}
}
/// 判断url是否有效
bool isUrlValidate = false;
if (isUrlExist) {
Uri? uri = Uri.tryParse(actualUrl);
if (uri != null) {
var host = uri.host;
if (host.isNotEmpty) {
isUrlValidate = true;
}
}
}
/// 如果没有指定占位图,默认给一个
actualPlaceholder = placeholder ?? R.assetsImg001;
if (isUrlValidate) {
return CachedNetworkImage(
cacheManager: CacheManager(
Config(
"CachedNetworkImageKey",
stalePeriod: const Duration(days: 3),
maxNrOfCacheObjects: 200,
),
),
imageUrl: actualUrl,
placeholder: (context, url) => _buildPlaceholderImage(actualPlaceholder),
errorWidget: (context, url, error) => _buildPlaceholderImage(actualPlaceholder),
width: width,
height: height,
fit: fit,
fadeOutDuration: const Duration(milliseconds: 100),
fadeInDuration: const Duration(milliseconds: 100),
// //占位符,根据加载的进度显示进度条
placeholderFadeInDuration: const Duration(seconds: 3),
);
} else {
return _buildPlaceholderImage(actualPlaceholder);
}
}
/// 占位图
Widget _buildPlaceholderImage(String name) {
return Image.asset(
name,
width: width,
height: height,
fit: fit,
);
}
}
3. 从相册读取照片
通常在设置页面,从本地相册读取图片,上传到后台,作为头像。从相册读取照片,有一个插件file_picker,点赞数还是很多的。
使用还是很方便的,就是提哦啊用一个方法,返回图片文件的本地路径
void onPhotoClick() async {
try {
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.image,
allowMultiple: false,
);
if (result != null) {
List paths = result.paths;
String imgPath = paths[0];
if (imgPath.isNotEmpty) {
LogUtil.log("图片路径:$imgPath");
RouteUtils.openChangeAvatar(imgPath);
}
}
} catch (e) {
ToastUtil.showText(text: "读取图片文件出错:${e.toString()}");
}
}
会弹出从相册取照片的弹窗:
返回的是一个数组,是本地图片的路径,这次只取一张,所以取第1个元素就可以了。
/Users/zxs/Library/Developer/CoreSimulator/Devices/915800E9-32EF-49CF-9410-EDD34A1A7BA6/data/Containers/Data/Application/114A6865-58C6-48BF-B088-3554C295780F/tmp/IMG_0005-1719389214783.jpeg
得到这个图片路径之后,将这个字符串的path转化为图片File类型,然后使用Image.file组件在本地显示。
/// 本地图片path转化为File结构
imgPath = Get.arguments?["imgPath"] ?? "";
if ((imgPath != null) && imgPath.isNotEmpty) {
imgFile = File(imgPath);
isDefaultAvatar = false;
update();
}
/// 在view中用 Image.file在本地显示图片
Image.file(
logic.imgFile,
width: ScreenUtil().screenWidth,
fit: BoxFit.cover,
),
选择的照片可以大图显示,等待用户确认:
4. 上传图片到阿里云
用户确认图片无误,点击“更换头像”按钮之后,接下来的操作应该是将本地图片上传到阿里云,然后后端返回用户新头像的URL,在设置页面显示用户的新头像。图片上传一般有两种做法:
- 方案1:通过Dio的表单数据的方式上传图片文件,然后由后端转存阿里云
final formData = FormData.fromMap({
'name': 'dio',
'date': DateTime.now().toIso8601String(),
'file': await MultipartFile.fromFile('./text.txt', filename: 'upload.txt'),
});
final response = await dio.post('/info', data: formData);
以前大多是这么做的,不过现在一般不这么做了,通过数据接口Post表单来传文件本身就不好,然后传到后端之后还要再传一次到阿里云,浪费严重。
- 方案2:阿里云图片上传插件将照片上传到阿里云,得到图片的url,然后调用接口告诉后端图片的URL。
上传图片到阿里云要用到相应的插件flutter_oss_aliyun,虽然点赞数很少,但是还是得用它。
step1: 调用后台接口1,获得阿里云访问的临时参数,有accessKeyId,accessKeySecret,securityToken等等
step2:调用插件的Client().putObjectFile的方法,将本地图片文件上传到阿里云,成功后会返回图片在阿里云的URL
step3:调用后台接口2,高度后端图片文件在阿里云的url,完成前后端的信息同步。
5. 拍照片
从相册读取照片和用照相机拍摄照片,都可以用同一个插件image_picker;这个插件的点赞数也是很多的
取照片和拍照可以通过参数区分,可以封装成工具类。返回的结果XFile中有字符串的path属性,就是本地图片文件的路径,和上一个插件的是一样的。
class ImageUtil {
/*
* 从相机取图片
*/
static Future getCameraImage() async {
return await ImagePicker().pickImage(source: ImageSource.camera);
}
/*
* 从相册取图片
*/
static Future getGalleryImage() async {
return await ImagePicker().pickImage(source: ImageSource.gallery);
}
}
6. 二维码扫描
这个也有相应的插件qr_code_scanner; 点赞数也是很多的
使用也很简单,它有现成的扫描界面,可以直接继承到页面中:
child: QRView(
key: qrKey,
onQRViewCreated: _onQRViewCreated,
),
扫描后的响应写在自定义的监听函数中:
void _onQRViewCreated(QRViewController controller) {
this.controller = controller;
controller.scannedDataStream.listen((scanData) {
setState(() {
result = scanData;
});
});
}
7. 大图展示
普通的图片,使用Image.asset组件基本差不多了。不过还有一类需求,就是大图展示,放大缩小,平移等操作,自己写就比较麻烦了。这里有个插件photo_view就可以直接用。点赞数也是比较多的
用的比较多的就是显示一组照片,可以做成一个工具组件:
import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';
@override
Widget build(BuildContext context) {
return Container(
child: PhotoViewGallery.builder(
scrollPhysics: const BouncingScrollPhysics(),
builder: (BuildContext context, int index) {
return PhotoViewGalleryPageOptions(
imageProvider: AssetImage(widget.galleryItems[index].image),
initialScale: PhotoViewComputedScale.contained * 0.8,
heroAttributes: PhotoViewHeroAttributes(tag: galleryItems[index].id),
);
},
itemCount: galleryItems.length,
loadingBuilder: (context, event) => Center(
child: Container(
width: 20.0,
height: 20.0,
child: CircularProgressIndicator(
value: event == null
? 0
: event.cumulativeBytesLoaded / event.expectedTotalBytes,
),
),
),
backgroundDecoration: widget.backgroundDecoration,
pageController: widget.pageController,
onPageChanged: onPageChanged,
)
);
}
8. 保存图片到相册
从网络下载图片,并保存到相册,这个需求虽然不常见,但是也会有。这里有一个插件image_gallery_saver可以做这个事,虽然点赞数不是很多,但是确实有用
一般结合Dio一起使用,从网络下载图片,然后保存到相册:
_saveNetworkImage() async {
var response = await Dio().get(
"https://ss0.baidu.com/94o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=a62e824376d98d1069d40a31113eb807/838ba61ea8d3fd1fc9c7b6853a4e251f94ca5f46.jpg",
options: Options(responseType: ResponseType.bytes));
final result = await ImageGallerySaver.saveImage(
Uint8List.fromList(response.data),
quality: 60,
name: "hello");
print(result);
}
9. 图片压缩
特别是iOS的照片,数据量很大,上传阿里云OSS之前最好压缩一下。阿里云默认限制最大的图片是20M,一般图片不要超过4M,不然的话就太大了,图片存储代价太高。
所以,压缩图片在某些时候还是需要的,这里有个现成的插件flutter_image_compress可以直接用。点赞数还是比较多的
使用也很简单:
// 1. compress file and get Uint8List
Future<Uint8List> testCompressFile(File file) async {
var result = await FlutterImageCompress.compressWithFile(
file.absolute.path,
minWidth: 2300,
minHeight: 1500,
quality: 80,
);
print(file.lengthSync());
print(result.length);
return result;
}