간단 요약
1. 게시물목록 ListView에 ScrollController() 부착하기
2. 스크롤위치 계속 감시해주는 리스너 부착하기
3. 맨 밑까지 스크롤하면 서버에 게시물 더 달라고 GET요청하기
4. 데이터 가져오면 data라는 state에 추가해주기
5. initState() 함수 기능 잘알아두기
import 'package:flutter/rendering.dart';
코드 최상단에 import.
var scroll = ScrollController();
ListView.builder(itemCount: 3, controller: scroll, itemBuilder: (c, i){
return 어쩌구저쩌구;
}
var scroll에 현재 스크롤의 정보가 담김!
void initState() {
super.initState();
scroll.addListener(() {
//scroll 이라는 변수가 바뀔 떄 마다 여기 코드를 실행해줌.
});
}
addListener은 성능을 많이 잡아 먹어서 나중에 쓸 일 없으면 없애도됨
void initState() {
super.initState();
scroll.addListener(() {
print(scroll.position.pixels);
});
}
// 스크롤 할 때 마다 좌표 값이 출력됨.
void initState() {
super.initState();
scroll.addListener(() {
print(scroll.position.maxScrollExtent);
// 유저가 스크롤 아래로 내리는지 위로 내리는지 알려주는 코드
// print(scroll.position.userScrollDirection);
});
}
// 스크롤 할 때 마다 최대 내릴 수 있는 좌표 값만 출력됨.
void initState() {
super.initState();
scroll.addListener(() {
if(scroll.position.pixels == scroll.position.maxScrollExtent) {
print("다 내려 옴");
}
}
});
}
스크롤이 다 내려오면 문자 출력.
이제 저기에 더보기 만들려면 프린트 대신에 서버에 get 요청 하면 되는거 아님?
import 'package:flutter/material.dart';
import './style.dart' as style;
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter/rendering.dart';
void main() {
runApp(
MaterialApp(
theme: style.theme,
home: MyApp()
)
);
}
class MyApp extends StatefulWidget {
MyApp({Key? key}) : super(key: key);
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var tab = 0;
var data = [];
var getCount = 1; // 2가 되면 더이상 더보기 안되게!(주목1)
addData(a) {
setState(() {
data.add(a); // 더보기 시 가져온 데이터를 기존 배열에 추가
});
}
getData() async {
var result = await http.get(Uri.parse('https://codingapple1.github.io/app/data.json'));
var result2 = jsonDecode(result.body);
setState(() {
data = result2;
});
}
getCountAdd () {
setState(() {
getCount++; // getCount가 2가 되면 더 이상 더보기 x(주목2)
});
}
//로드 될 때 실행되는 코드
void initState() {
super.initState();
getData();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Instagram') ,
actions: [
IconButton(
onPressed: (){},
icon: Icon(Icons.add_box_outlined),
iconSize: 30,
)
],
),
//사용할 State 보내기
body: [Home(data : data, addData : addData, getCount : getCount, getCountAdd : getCountAdd,), Text('샵페이지')][tab],
bottomNavigationBar: BottomNavigationBar(
showSelectedLabels: false,
showUnselectedLabels: false,
// items를 눌렀을 때 보여 줄 아이콘의 값
// 1번 items를 눌렀을 때 tab의 값인 1임
currentIndex : tab,
onTap: (i){
setState(() {
tab = i;
});
},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home_outlined),
activeIcon: Icon(Icons.home), //눌렀을 때 보여줄 아이콘
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.shopping_bag_outlined),
activeIcon: Icon(Icons.shopping_bag), //눌렀을 때 보여줄 아이콘
label: 'Shop',
),
],
),
);
}
}
class Home extends StatefulWidget { // 부모 클래스에서 가져 올 변수와 함수 추가
Home({Key? key, this.data, this.addData, this.getCount, this.getCountAdd}) : super(key: key);
final data;
final addData;
final getCount; // 더보기 상태 변수
final getCountAdd; // 더보기 상태 변수 증가
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
var scroll = ScrollController();
getMore() async { // get으로 서버에 데이터 가져오기
var result = await http.get(Uri.parse('https://codingapple1.github.io/app/more1.json'));
var result2 = jsonDecode(result.body);
widget.addData(result2); // 가져온 값을 기존 배열에 추가
}
getMore2() async {
var result = await http.get(Uri.parse('https://codingapple1.github.io/app/more2.json'));
var result2 = jsonDecode(result.body);
widget.addData(result2);
}
void initState() { // 하단의 6. 문제 발생 및 해결 참조
super.initState();
scroll.addListener(() {
//스크롤이 최대로 내려오면 서버에 get 요청하여 기존배열에 데이터 추가.
// 만약 getCount가 2면 더 이상 서버에 데이터 요청하지 않음.
if(scroll.position.pixels == scroll.position.maxScrollExtent) {
if(widget.getCount == 1) {
getMore();
widget.getCountAdd();
} else if (widget.getCount == 2) {
getMore2();
widget.getCountAdd();
} else {
print('끝');
}
}
});
}
Widget build(BuildContext context) {
if(widget.data.isNotEmpty) { // 데이터 안가져오면 로딩바 보여주기
// controller: scroll로 스크롤 정보 가져 오기
return ListView.builder(itemCount: widget.data.length, controller: scroll, itemBuilder: (c, i){
return Column(
children: [
Image.network(widget.data[i]['image']),
Container(
constraints: BoxConstraints(maxWidth: 600),
padding: EdgeInsets.all(20),
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('좋아요 ${widget.data[i]['likes']}'),
Text(widget.data[i]['user']),
Text(widget.data[i]['content']),
Text(widget.data[i]['date']),
],
),
)
],
);
});
} else {
return CircularProgressIndicator(); // 로딩 바
}
}
}
6. 무한 더보기 문제 발생 및 원인
- 최상단 MyApp 위젯의 코드 가독성을 위해
Home Class 안에서 getMore() 이라는 코드를 생성 했듯이
더보기를 계속 할지를 정하는 getCount 변수와 getCountAdd()함수를
Home Class의 "void initState() {}" 여기에 선언함.
- print로 확인해보니 getCount의 값은 바뀌었는데
else if에 준 조건인 getMore2() 함수가 아닌
getMore() 함수만 계속 실행되어 무한 더보기가 되었다.
- 원인 :
initState는 계속 실행해주는 함수라 내부에 변수를 선언하면 계~속
변수의 값을 유지하여 getCount의 값이 변하지 않았던거임~~
print해보면 변한 값(2)이 나오길래
"뭐지? 변수 값은 변하는데 else if로 준 코드가 실행이 안되넹; 노잼~"
7. 해결
- 부모 클래스인 MyApp내부에 getCount변수와 getCountAdd() 함수를 다시 선언하여 해결!
위의 코드를 보면 MyApp 클래스에 선언되어 있다.
8. 결론
- 변수 및 함수 선언시 내가 어느 기능을 하는 함수에 선언하고 있는지
해당 함수의 기능과 위치를 잘 고구려 하자.