일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Computer Architecture
- DART
- rao
- study book
- 영상처리
- Algorithm
- Flutter
- 파이토치 트랜스포머를 활용한 자연어 처리와 컴퓨터비전 심층학습
- BAEKJOON
- BFS
- system hacking
- ML
- FastAPI
- C++
- Got
- ARM
- PCA
- Stream
- 백준
- llm을 활용 단어장 앱 개발일지
- fastapi를 사용한 파이썬 웹 개발
- bloc
- MATLAB
- Image Processing
- pytorch
- Kaggle
- MDP
- Dreamhack
- Widget
- BOF
- Today
- Total
Bull
[Flutter::Widget] Netiveview(ImagePicker)에서 상태 변경으로 이미지가 렌더링될 때 스크롤 하는 방법 본문
[Flutter::Widget] Netiveview(ImagePicker)에서 상태 변경으로 이미지가 렌더링될 때 스크롤 하는 방법
Bull_ 2024. 9. 1. 11:09내가 하고 있는 아르바이트의 수강생의 질문이었다. ImagePicker에서 이미지를 로드하고 상태 변화에 따라 위젯에 사진을 추가하는 로직인데 이 때 사진의 높이가 크면 스크롤뷰를 통해서 아래 구간에 공간을 추가하고 스크롤을 내려서 확인할 수 있는 위젯이다.
이때 scrollToBottom 메소드를 호출해서 스크롤을 아래쪽으로 자동 모션을 진행한다. 하지만 그래서 버튼 부분에 await을 통해 chooseImage를 호출하고 scrollToBottom을 호출하는 형식으로 진행했다.
하지만 이 이론에서 버그가 생겼다. 이미지 로드 -> 상태 변화 감지 -> 자동 스크롤 -> 재렌더링으로 일어나기 때문이다. 이렇게 되면 사진을 추가해도 재렌더링되기 전의 상태에서 scrollToBottom을 호출하기 때문에 스크롤을 내려도 변화가 없다. 이후에 재렌더링이 되기 때문에 원하는 방식을 어느 부분에서 호출을 하여도 이룰 수 없었다.
여러 클래스를 적용하던 중 Image.File
에 있는 frameBuilder
프로퍼티를 찾았다. 이 빌더를 통해 이미지 프레임이 빌드된 이후의 상태를 체크할 수 있다. 그러면 이 스코프안에 scrollToBottom을 호출하면 우리가 원했던 방향으로 나아갈 수 있다.
frameBuilder: (BuildContext context, Widget child,
int? frame, bool wasSynchronouslyLoaded) {
if (frame != null || wasSynchronouslyLoaded) {
widget.scrollToBottom();
}
return child;
}
wasSynchronouslyLoaded
: 이미지가 동기적으로 로드되었는지 확인할 수 있다.frame
: 이미지가 몇 번째 프레임되고 있는지 확인할 수 있다. 일반적으로 gif처럼 움짤이 아니므로 단일 프레임으로 사용되기 때문에 null이 아님을 체크해주면 된다.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
void main() {
runApp(MaterialApp(
home: ParentWidget(),
));
}
class ParentWidget extends StatefulWidget {
@override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
final ScrollController _scrollController = ScrollController();
void scrollToBottom() {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Scroll Test"),
),
body: SingleChildScrollView(
controller: _scrollController,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ChildWidget(scrollToBottom: scrollToBottom),
],
),
),
),
);
}
}
class ChildWidget extends StatefulWidget {
final VoidCallback scrollToBottom;
const ChildWidget({required this.scrollToBottom, Key? key}) : super(key: key);
@override
_ChildWidgetState createState() => _ChildWidgetState();
}
class _ChildWidgetState extends State<ChildWidget> {
File? imageFile;
Future<void> chooseImage(ImageSource source) async {
try {
final XFile? pickedImage = await ImagePicker().pickImage(source: source);
if (pickedImage != null) {
setState(() {
imageFile = File(pickedImage.path);
});
}
} catch (errorMsg) {
print(errorMsg.toString());
}
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ElevatedButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return SimpleDialog(
title: Text('Select Image'),
children: [
SimpleDialogOption(
child: Text('From Gallery'),
onPressed: () async {
Navigator.pop(context);
await chooseImage(ImageSource.gallery);
},
),
SimpleDialogOption(
child: Text('From Camera'),
onPressed: () async {
Navigator.pop(context);
await chooseImage(ImageSource.camera);
},
),
],
);
},
);
},
child: Text("Choose Image"),
),
SizedBox(height: 400),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.blueAccent),
borderRadius: BorderRadius.all(Radius.circular(5)),
),
child: imageFile == null
? Center(child: Text("No image selected"))
: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
imageFile!,
frameBuilder: (BuildContext context, Widget child,
int? frame, bool wasSynchronouslyLoaded) {
if (frame != null || wasSynchronouslyLoaded) {
widget.scrollToBottom();
}
return child;
},
),
),
),
],
);
}
}
'Software Framework > Flutter' 카테고리의 다른 글
[Flutter:: Widget] ReorderableListView 수동 정렬하기 (0) | 2024.09.26 |
---|---|
[Flutter] 부모위젯이 자식위젯의 method 호출 방법 (0) | 2024.09.07 |
[Flutter::Widget] StatefulBuilder (0) | 2024.08.27 |
[Flutter::Widget] Dialog 종류 (0) | 2024.08.27 |
[Flutter::State] ChangeNotifierProvider와 Provider 차이 (0) | 2024.08.23 |