Flutter 스크롤 이벤트 주고 더보기 요청 해보기

바다구름·2023년 3월 9일
0

Flutter

목록 보기
13/19

간단 요약
1. 게시물목록 ListView에 ScrollController() 부착하기
2. 스크롤위치 계속 감시해주는 리스너 부착하기
3. 맨 밑까지 스크롤하면 서버에 게시물 더 달라고 GET요청하기
4. 데이터 가져오면 data라는 state에 추가해주기
5. initState() 함수 기능 잘알아두기

1. main.dart 파일에 import하기

import 'package:flutter/rendering.dart';

코드 최상단에 import.


2. StatefulWidget 안에 코드 작성

  • 첫 class 안에 있던 변수 사용은 widget.변수명
  • 스크롤 변수 생성
var scroll = ScrollController();

  • 스크롤은 ListView.builder에 의해 생기기 때문에 ListView.builder()위젯 안에 controller: scroll 코드 추가
ListView.builder(itemCount: 3, controller: scroll, itemBuilder: (c, i){
	return 어쩌구저쩌구;
}

var scroll에 현재 스크롤의 정보가 담김!



3. addListener 사용하기

  • 근데 프린트로 scroll 출력하면 0만 뜲.
    위치 측정은 스크롤 "움직일 때마다" 해야함.
  
  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); 
    });
  }
  
 // 스크롤 할 때 마다 최대 내릴 수 있는 좌표 값만 출력됨.

4. 스크롤이 다 내려 왔는지 확인하기.

  
  void initState() {
    super.initState();
    scroll.addListener(() {
      if(scroll.position.pixels == scroll.position.maxScrollExtent) {
        print("다 내려 옴");
        } 
      }
    });
  }

스크롤이 다 내려오면 문자 출력.
이제 저기에 더보기 만들려면 프린트 대신에 서버에 get 요청 하면 되는거 아님?

5. 더보기 만들기 코드

  • 하단의 class Home extends StatefulWidget {} 코드 위주로 보자.
  • getCount와 getCountAdd도 주목 해주셈.
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. 결론

  • 변수 및 함수 선언시 내가 어느 기능을 하는 함수에 선언하고 있는지
    해당 함수의 기능과 위치를 잘 고구려 하자.
profile
안녕하세요.

0개의 댓글