美文网首页Flutter开发
Flutter之图片相关 2024-06-25 周二

Flutter之图片相关 2024-06-25 周二

作者: 勇往直前888 | 来源:发表于2024-06-25 17:44 被阅读0次

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;
  }

相关文章

网友评论

    本文标题:Flutter之图片相关 2024-06-25 周二

    本文链接:https://www.haomeiwen.com/subject/sdldcjtx.html