Flutter 圖片選擇器 SelectPhotoWidget

早起的年輕人發表於2021-07-04

在碼農的世界裡,優美的應用體驗,來源於程式設計師對細節的處理以及自我要求的境界,年輕人也是忙忙碌碌的碼農中一員,每天、每週,都會留下一些腳印,就是這些創作的內容,有一種執著,就是不知為什麼,如果你迷茫,不妨來瞅瞅碼農的軌跡。

如果你有興趣 你可以關注一下公眾號 biglead 來獲取最新的學習資料。


先來看看本文章實現的效果

在這裡插入圖片描述

直接來看程式碼吧

首先是啟動函式

main() {
  runApp(MaterialApp(
    //不顯示 debug標籤
    debugShowCheckedModeBanner: false,
    //顯示的首頁面
    home: DemoSelectImageWidgetPage(),
  ));
}
複製程式碼

然後是這個首頁面,核心程式碼就是 SelectPhotoWidget 這個元件

///程式碼清單
class DemoSelectImageWidgetPage extends StatefulWidget {
  @override
  _DemoSelectImageWidgetPageState createState() =>
      _DemoSelectImageWidgetPageState();
}

class _DemoSelectImageWidgetPageState extends State<DemoSelectImageWidgetPage> {
  @override
  Widget build(BuildContext context) {
    //
    return Scaffold(
      backgroundColor: Colors.grey,
      appBar: AppBar(title: Text("圖片選擇元件")),
      body: Center(
        child: Container(
          padding: EdgeInsets.all(12),
          //圖片選擇元件
          child: SelectPhotoWidget(
            header: Text(
              "請選擇照片",
              style: TextStyle(fontWeight: FontWeight.w600, fontSize: 18),
            ),
            //標題下的紅色提醒文字
            tips: "請注意 最多選擇5張圖片",
            //圖片選擇回撥
            imageSelectAction: (List<String> list) {
              print("實時選擇回撥${list.toString()}");
            },
            //最大選擇圖片資料
            maxSelect: 6,
            //預設圖片
            imageList: [],
          ),
        ),
      ),
    );
  }
}

複製程式碼

將核心功能封裝在了 SelectPhotoWidget 元件中,大家可以直接複製使用

///
class SelectPhotoWidget extends StatefulWidget {
  ///每次點選選擇圖片後的回撥
  final Function(List<String>) imageSelectAction;

  ///自定義標題
  final Widget header;

  ///標題下的小捍
  final String tips;

  ///預顯示使用的圖片
  final List<String> imageList;

  ///最多可選擇的圖片數量
  final int maxSelect;

  ///為true 時顯示使用網路圖片
  final ImageType imageType;

  const SelectPhotoWidget(
      {Key key,
      this.header,
      this.tips,
      this.imageList,
      this.imageType = ImageType.asset,
      this.imageSelectAction,
      this.maxSelect = 5})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return _SelectPhotoWidgetState();
  }
}
複製程式碼
class _SelectPhotoWidgetState extends State<SelectPhotoWidget>
    with WidgetsBindingObserver {
  ///當前是否正在選擇圖片
  bool _isSelect = false;

  @override
  void initState() {
    super.initState();
    if (widget.imageList != null) {
      //判斷一下最大選擇圖片資料
      if (widget.imageList.length <= widget.maxSelect) {
        _imageList = widget.imageList;
      } else {
        //擷取圖片
        _imageList = widget.imageList.sublist(0, widget.maxSelect);
      }
    }
    //繫結檢視監聽
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.inactive:
        // 處於這種狀態的應用程式應該假設它們可能在任何時候暫停。
        break;
      case AppLifecycleState.resumed:
        //從後臺切換前臺,介面可見
        break;
      case AppLifecycleState.paused:
        // 介面不可見,後臺
        break;
      case AppLifecycleState.detached:
        // APP結束時呼叫
        break;
    }
  }

  @override
  void dispose() {
    //解綁檢視監聽
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    //圓角矩形剪裁
    return ClipRRect(
      //圓角
      borderRadius: BorderRadius.all(Radius.circular(12)),
      child: Container(
        color: Color(0xffFFFFFF),
        //寬度填充
        width: double.infinity,
        //統一內邊距
        padding: EdgeInsets.all(10),
        //垂直方向的線性排列
        child: Column(
          //水平方向
          crossAxisAlignment: CrossAxisAlignment.start,
          //包裹
          mainAxisSize: MainAxisSize.min,
          children: [
            //標題
            buildHeaderWidget(),
            //第二行的小提示
            buildTipsWidget(),
            //顯示的圖片
            buildGridView(),
            SizedBox(
              height: 10,
            ),
          ],
        ),
      ),
    );
  }

  buildHeaderWidget() {
    return widget.header != null ? widget.header : Container();
  }

  buildTipsWidget() {
    if (widget.tips == null || widget.tips.length == 0) {
      return Container();
    }
    return Container(
      padding: EdgeInsets.only(top: 10, bottom: 16),
      //圓角矩形裁剪
      child: ClipRRect(
        //圓角
        borderRadius: BorderRadius.all(Radius.circular(12)),
        child: Container(
          padding: EdgeInsets.only(left: 10, right: 10, top: 6, bottom: 6),
          color: Color(0xffFFF1F1),
          child: Text(
            "${widget.tips}",
            style: TextStyle(
              color: Color(0xffBD2F2F),
              fontSize: 14,
            ),
          ),
        ),
      ),
    );
  }

  List<String> _imageList = [];

  buildGridView() {
    return Container(
      child: GridView.builder(
        padding: EdgeInsets.only(top: 8, bottom: 8),
        //包裹
        shrinkWrap: true,
        //不可滑動
        physics: NeverScrollableScrollPhysics(),
        //圖片個數
        itemCount: getSelectCount(),
        //SliverGridDelegateWithFixedCrossAxisCount 構建一個橫軸固定數量Widget
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            //橫軸元素個數
            crossAxisCount: 4,
            //主軸間距
            mainAxisSpacing: 1.0,
            //從軸間距
            crossAxisSpacing: 1.0,
            //子元件寬高長度比例
            childAspectRatio: 1.0),
        itemBuilder: (BuildContext context, int index) {
          //Widget Function(BuildContext context, int index)
          if (index == _imageList.length) {
            if (_isSelect) {
              return Center(child: Text("..."));
            }
            return Container(
              margin: EdgeInsets.only(top: 10),
              child: IconButton(
                icon: Icon(Icons.add),
                onPressed: () {
                  onSelectImageFunction();
                },
              ),
              color: Color(0xFFF1F1F2),
            );
          }
          //顯示當前的圖片
          String imageUrl = _imageList[index];
          return Container(
            //層疊佈局
            child: Stack(
              children: [
                //向左下偏移一點
                Positioned.fill(
                  top: 10,
                  right: 10,
                  child: GestureDetector(
                    onTap: () {
                      //檢視大圖
                    },
                    child: Container(
                      padding: EdgeInsets.all(1),
                      child: buildImageWidget(imageUrl),
                      color: Colors.grey[200],
                    ),
                  ),
                ),
                Positioned(
                  top: 0,
                  right: 0,
                  child: GestureDetector(
                    onTap: () {
                      onDeleteImageFunction(index);
                    },
                    child: ClipOval(
                      child: Container(
                        padding: EdgeInsets.all(2),
                        color: Colors.red,
                        child: Icon(
                          Icons.close,
                          color: Colors.white,
                          size: 14,
                        ),
                      ),
                    ),
                  ),
                ),
              ],
            ),
          );
        },
      ),
    );
  }

  Widget buildImageWidget(String image) {
    if (widget.imageType == ImageType.net) {
      return Image.network(
        image,
        fit: BoxFit.fitWidth,
      );
    } else if (widget.imageType == ImageType.asset) {
      return Image.asset(
        image,
        fit: BoxFit.fitWidth,
      );
    }
    return Image.file(
      File(image),
      fit: BoxFit.fitWidth,
    );
  }

  ///最大選擇圖片資料限制
  getSelectCount() {
    if (_imageList.length >= widget.maxSelect) {
      return widget.maxSelect;
    }
    return _imageList.length + 1;
  }

  //刪除照片
  void onDeleteImageFunction(int index) {
    _imageList.removeAt(index);
    setState(() {});
    widget.imageSelectAction(_imageList);
  }

  void onSelectImageFunction() async {
    _isSelect = true;
    setState(() {});
    String localImageUrl = "assets/images/sp03.png";
    await Future.delayed(Duration(milliseconds: 1000));
    _isSelect = false;
    if (localImageUrl.length > 0) {
      _imageList.add(localImageUrl);
      setState(() {});
      widget.imageSelectAction(_imageList);
    }
  }
}
複製程式碼

相關文章