Flutter 相冊圖片多選

前一段項目要做類似微信朋友圈的評論回復(fù)功能垦细,要多選圖片捺典,當(dāng)時在網(wǎng)上也找了一下浅碾,發(fā)現(xiàn)文章并不是太多大州,就把自己寫的也記錄一下(主要是我們的項目使用的flutter版本太低了1.17.2的,flutter2.0.1版本之上可使用images_picker插件,一個插件滿足您的需求)垂谢。

插件

dependencies:
  photo:
    path: ./flutter_photo #這個插件pub_dev上也不是最新的厦画,我是把作者發(fā)布到git上的拉下來導(dǎo)入到項目中了
  image_picker: ^0.6.7+22

為什么要用兩個插件,是因為image_picker這個插件雖然支持拍照和相冊選擇滥朱,但是圖片只能一張一張的選擇根暑,photo是支持圖片多選的(但是這個插件好久沒更新了)

廢話不多說,開始上代碼:

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_app_image_picker/photo_picker_tool.dart';
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {


  @override
  Widget build(BuildContext context) {

    return Scaffold(
        appBar:AppBar(
            title:Text('PhotoSelectTest')
        ),
        body:
        Container(
            padding: EdgeInsets.fromLTRB(80, 10, 30, 10),
            color: Colors.red,
            child:
            JhPhotoPickerTool(
              lfPaddingSpace: 110,
              callBack: (var img) async{
                print("img-------${File(img[0]).lengthSync()}------");
                print(img.length);
                print(img);
              },
            )
        )

    );
  }
}


import 'package:flutter/material.dart';
import 'package:jxcapp/utils/image_utils.dart';

import 'package:photo/photo.dart';
import 'package:photo_manager/photo_manager.dart';
import 'dart:io';
import 'package:image_picker/image_picker.dart';
import 'package:heic_to_jpg/heic_to_jpg.dart';
const double itemSpace = 10.0;
const double space = 5.0; //上下左右間距
const double deleBtnWH = 20.0;
const Color bgColor = Colors.white;
const int maxCount = 3;// 最大選擇圖片數(shù)量
typedef CallBack = void Function(List imgData);

class JhPhotoPickerTool extends StatefulWidget {
  final double lfPaddingSpace; //外部設(shè)置的左右間距
  final CallBack callBack;

  JhPhotoPickerTool({
    this.lfPaddingSpace,
    this.callBack,
  });

  @override
  _JhPhotoPickerToolState createState() => _JhPhotoPickerToolState();
}

class _JhPhotoPickerToolState extends State<JhPhotoPickerTool> {
  List _imgData = List(); //圖片list
  List imgDefaultData = List(); //圖片list
  List<AssetEntity> imgPicked = [];

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    imgDefaultData.add("selectPhoto_add"); //先添加 加號按鈕 的圖片
  }

  @override
  void setState(fn) {
    // TODO: implement setState
    super.setState(fn);
    List data = List();
    data.addAll(_imgData);
    // data.removeAt(_imgData.length - 1);
    widget.callBack(data);
  }

  @override
  Widget build(BuildContext context) {
    var kScreenWidth = MediaQuery.of(context).size.width;

    var lfPadding = widget.lfPaddingSpace == null ? 0.0 : widget.lfPaddingSpace;
    var ninePictureW = (kScreenWidth - space * 2 - 2 * itemSpace - lfPadding);
    var itemWH = ninePictureW / maxCount;
    int columnCount = _imgData.length > 6 ? 3 : _imgData.length <= 3 ? 1 : 2;

    return Container(
        color: bgColor,
        width: kScreenWidth - lfPadding,
        height:
            columnCount * itemWH + space * 2 + (columnCount - 1) * itemSpace,
        child: GridView.builder(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              //可以直接指定每行(列)顯示多少個Item
              //一行的Widget數(shù)量
              crossAxisCount: 3,
              crossAxisSpacing: itemSpace, //水平間距
              mainAxisSpacing: itemSpace, //垂直間距
              childAspectRatio: 1.0, //子Widget寬高比例
            ),
            physics: NeverScrollableScrollPhysics(),
            padding: EdgeInsets.all(space),
            //GridView內(nèi)邊距
            itemCount: _imgData.length== maxCount?_imgData.length: (_imgData.length + imgDefaultData.length),
            itemBuilder: (context, index) {
              if (_imgData.length == maxCount) {
                return imgItem(index, setState, _imgData, imgPicked);
              } else {
                if (index == _imgData.length) {
                  return addBtn(context, setState, imgDefaultData, imgPicked);
                } else {
                  return imgItem(index, setState, _imgData, imgPicked);
                }
              }
            }));
  }

  /** 添加按鈕 */
  Widget addBtn(context, setState, imgData, imgPicked) {
    return GestureDetector(
      child: Container(
        color: Color(0xffF7F7F7),
        padding: EdgeInsets.all(40),
        child: Image.asset(
          ImageUtils.getImgPath('report/report_add_icon'),
          // fit: BoxFit.cover,
        ),
      ),
      onTap: () {
        FocusScope.of(context).requestFocus(FocusNode());
        showModalBottomSheet(
          context: context,
          builder: (BuildContext context) {
            return new Container(
              height: 195.0,
              child: Column(
                children: <Widget>[
                  MaterialButton(
                    height:50,
                    child: Text('拍攝'),
                    onPressed: () async {
                      Navigator.pop(context);
                      var image = await ImagePicker.pickImage(
                          source: ImageSource.camera);
                      print(image);
                      if(image.absolute.path.contains('.he') || image.absolute.path.contains('.HE')){
                        String jpegPath = await HeicToJpg.convert(image.absolute.path);
                        _imgData.insert(_imgData.length, jpegPath);
                      }
                      else{
                        _imgData.insert(_imgData.length, image.absolute.path);
                      }
                      // _imgPicked.add(image);

                      setState(() {});

                    },
                  ),
                  SizedBox(
                    height: 1,
                    child: Container(
                      color: Color(0xffF4F4F4),
                    ),
                  ),
                  MaterialButton(
                    height:50,
                    child: Text('從手機相冊選擇'),
                    onPressed: () async {
                      pickAsset(context, setState, _imgData, imgPicked);
                      Navigator.pop(context);
                    },
                  ),
                  SizedBox(
                    height: 10,
                    child: Container(
                      color: Color(0xffF4F4F4),
                    ),
                  ),
                  MaterialButton(
                    height:50,
                    child: Text('取消'),
                    onPressed: () {
                      Navigator.pop(context);
                    },
                  ),
                ],
              ),
            );
          },
        ).then((val) {
          print(val);
        });
      },
    );
  }

  /** 多圖選擇 */
  void pickAsset(context, setState, imgData, imgPicked) async {
    final result = await PhotoPicker.pickAsset(
        context: context,
        pickedAssetList: imgPicked,
        maxSelected: maxCount - _imgData.length,
        pickType: PickType.onlyImage);

    if (result != null && result.isNotEmpty) {
      for (var e in result) {
        var file = await e.file;
//         if (!imgData.contains(file.absolute.path)) {// 如果想避免重復(fù)選同一張圖片徙邻,可以加上這個判斷
        if(file.absolute.path.contains('.he') || file.absolute.path.contains('.HE')){// 這個判斷的意義請繼續(xù)往下看
          String jpegPath = await HeicToJpg.convert(file.absolute.path);
          _imgData.insert(_imgData.length, jpegPath);
        }
        else{
          _imgData.insert(_imgData.length, file.absolute.path);
        }

        // }
      }
    }
    setState(() {});
  }

  /** 圖片和刪除按鈕 */
  Widget imgItem(index, setState, imgData, imgPicked) {
    return GestureDetector(
      child: Container(
        color: Colors.transparent,
        child: Stack(alignment: Alignment.topRight, children: <Widget>[
          ConstrainedBox(
            child: Image.file(File(imgData[index]), fit: BoxFit.cover),
            constraints: BoxConstraints.expand(),
          ),
          GestureDetector(
            child: Image.asset(
              ImageUtils.getImgPath('report/report_delete'),//這是我本地的添加圖片
              // fit: BoxFit.cover,
            ),
            onTap: () {
              //點擊刪除按鈕
              setState(() {
                _imgData.removeAt(index);
                // imgPicked.removeAt(index);
              });
            },
          )
        ]),
      ),
      onTap: () {
        print("點擊第${index}張圖片");
      },
    );
  }
}

以上就是我的代碼排嫌,可能有的朋友就問了heic_to_jpg這個插件是干什么用的, 蘋果heic格式圖片不能直接在瀏覽器上顯示缰犁,所以我們就需要借用插件轉(zhuǎn)換一下格式淳地,詳細請看:

heic:https://baike.baidu.com/item/HEIC/10444257
jpg: https://baike.baidu.com/item/JPEG%E6%A0%BC%E5%BC%8F/3462770
HEIF & HEVC https://juejin.im/post/59ddc13ff265da432319f438

到這里還不算結(jié)束怖糊,安卓還有一個問題,就是系統(tǒng)是安卓11及以上相冊打不開颇象,你需要修改你項目中的這個地方伍伤,上圖:

image.png

這里就是去除安卓11的新特性,可以使插件正常使用遣钳。

以上就是這篇文章的全部內(nèi)容了扰魂,寫的不好的地方還請多多指教!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耍贾,一起剝皮案震驚了整個濱河市阅爽,隨后出現(xiàn)的幾起案子路幸,更是在濱河造成了極大的恐慌荐开,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件简肴,死亡現(xiàn)場離奇詭異晃听,居然都是意外死亡,警方通過查閱死者的電腦和手機砰识,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門能扒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人辫狼,你說我怎么就攤上這事初斑。” “怎么了膨处?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵见秤,是天一觀的道長。 經(jīng)常有香客問我真椿,道長鹃答,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任突硝,我火速辦了婚禮测摔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘解恰。我一直安慰自己锋八,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布护盈。 她就那樣靜靜地躺著挟纱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪黄琼。 梳的紋絲不亂的頭發(fā)上樊销,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天整慎,我揣著相機與錄音,去河邊找鬼围苫。 笑死裤园,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的剂府。 我是一名探鬼主播拧揽,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼腺占!你這毒婦竟也來了淤袜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤衰伯,失蹤者是張志新(化名)和其女友劉穎铡羡,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體意鲸,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡烦周,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了怎顾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片读慎。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖槐雾,靈堂內(nèi)的尸體忽然破棺而出夭委,到底是詐尸還是另有隱情,我是刑警寧澤募强,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布株灸,位于F島的核電站,受9級特大地震影響钻注,放射性物質(zhì)發(fā)生泄漏蚂且。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一幅恋、第九天 我趴在偏房一處隱蔽的房頂上張望杏死。 院中可真熱鬧,春花似錦捆交、人聲如沸淑翼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽玄括。三九已至,卻和暖如春肉瓦,著一層夾襖步出監(jiān)牢的瞬間遭京,已是汗流浹背胃惜。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留哪雕,地道東北人船殉。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像斯嚎,于是被迫代替她去往敵國和親利虫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容