이번에는 InputScreen 의 image picker 부분을 구현해보겠습니다.
개발환경 : 윈도우11, 안드로이드 스튜디오, flutter 3.0.1
오늘 구현한 화면은 아래와 같습니다.
첫번째 이미지 - 이전에 구현한 더미 이미지 layout
두번째 이미지 - image picker 구현후 layout
첫번째 이미지 - image picker 로 사진을 선택후 layout
사용한 패키지는
image_picker: ^0.8.5+3
./src/screens/input/multi_image_select.dart
import 'dart:typed_data';
import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import '../../constants/common_size.dart';
class MultiImageSelect extends StatefulWidget {
const MultiImageSelect({Key? key}) : super(key: key);
@override
State<MultiImageSelect> createState() => _MultiImageSelectState();
}
class _MultiImageSelectState extends State<MultiImageSelect> {
bool _isPickingImages = false;
final List<XFile> _images = [];
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
Size _size = MediaQuery.of(context).size;
var imgSize = (_size.width / 3) - padding_16 * 2;
return SizedBox(
height: _size.width / 3,
// width: _size.width,
child: ListView(
// 기본 스크롤 방향을 위/아래 에서 좌/우로 변경해줌,
// 사이즈를 강제로 설정해야 함. 그래서 SizedBox 로 wrapping,
scrollDirection: Axis.horizontal,
children: <Widget>[
Padding(
// 패딩 - 여기서는 전체를 16으로 처리해서 아래는 위/아래/오른쪽만 16 처리할 것,
padding: const EdgeInsets.all(padding_16),
child: InkWell(
// onTap 에서 이미지를 List<XFile> 타입 배열에 추가,
onTap: () async {
_isPickingImages = true;
setState(() {});
final ImagePicker _picker = ImagePicker();
// Pick multiple images
final List<XFile>? images = await _picker.pickMultiImage(imageQuality: 20);
if (images != null && images.isNotEmpty) {
_images.clear();
_images.addAll(images);
}
_isPickingImages = false;
setState(() {});
},
child: Container(
width: imgSize,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(padding_16),
border: Border.all(color: Colors.grey, width: 1),
),
// 첫번째 사진 아이콘 부분 구현
child: _isPickingImages
? const Padding(
padding: EdgeInsets.all(padding_16 * 2),
child: CircularProgressIndicator(),
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.camera_alt_rounded, color: Colors.grey),
Text('0/10', style: Theme.of(context).textTheme.subtitle2),
],
),
),
),
),
// 중복되는 사진 부분 구현
// ... 으로 시작하면 기존 리스트에 새로운 결과(List.generate 결과)를 추가할 수 있다.
...List.generate(
_images.length,
(index) => Stack(
children: [
Padding(
// 패딩 - 위에서 전체를 16으로 처리해서 여기서는 위/아래/오른쪽만 16 처리할 것,
padding: const EdgeInsets.only(
right: padding_16, top: padding_16, bottom: padding_16),
// child: ExtendedImage.network(
// 변수(메모리)에 있는 이미지를 보여주기 위해서 ExtendedImage.memory 사용하고,
// 이미지를 화면에 보여주려면 FutureBuilder 로 wrapping 필요함
child: FutureBuilder<Uint8List>(
future: _images[index].readAsBytes(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ExtendedImage.memory(
snapshot.data!,
width: imgSize,
height: imgSize,
fit: BoxFit.cover,
loadStateChanged: (state) {
switch (state.extendedImageLoadState) {
case LoadState.loading:
return const Center(child: CircularProgressIndicator());
case LoadState.completed:
return null;
case LoadState.failed:
return const Icon(Icons.cancel);
}
},
// shape 을 지정해야만 borderRadius 설정이 정상 동작함,
borderRadius: BorderRadius.circular(padding_16),
shape: BoxShape.rectangle,
);
} else {
return const CircularProgressIndicator();
}
}),
),
Positioned(
top: 0,
right: 0,
// IconButton 의 기본 사이즈는 24 이므로 패딩 8 을 더해서 40 으로 설정,
width: 40,
height: 40,
child: IconButton(
padding: const EdgeInsets.all(8),
onPressed: () {
debugPrint('remove picture $index');
_images.removeAt(index);
setState(() {});
},
icon: const Icon(Icons.remove_circle),
color: Colors.black54,
),
),
],
),
),
],
),
);
},
);
}
}
'Flutter > 12 Clone 'Used Goods app'' 카테고리의 다른 글
[Flutter] Clone - 당근마켓32(ItemModel) (0) | 2022.08.12 |
---|---|
[Flutter] Clone - 당근마켓31(InputScreen - image picker upgrade) (0) | 2022.08.11 |
[Flutter] Clone - 당근마켓29(InputScreen - category 구현) (0) | 2022.08.11 |
[Flutter] Clone - 당근마켓28(InputScreen - layout/나머지 부분) (0) | 2022.08.10 |
[Flutter] Clone - 당근마켓27(InputScreen - layout/제거버튼) (0) | 2022.08.08 |