📘 Flutter

[Flutter] Future 를 사용한 Http 통신, ScrollController 를 사용한 최상단/최하단 강제 스크롤

핑크빛연어 2021. 8. 10. 18:03

 

Future 란?

 

지금은 없지만 미래에 작업이 수행되어 결과를 나타낼 것이라는 상태를 나타냄.

future 는 비동기 작업의 결과를 나타내며 미완료(데이터 value를 생성하기 전) 또는 완료(데이터 value 생성)의 두 가지 상태를 가질 수 있다.

미완료 : 비동기 함수를 호출하면 데이터 value 가 생성되기 전인 완료되지 않은 미래가 반환된다. future 는 비동기 함수의 작업이 완료되거나 오류가 발생하기를 기다리고 있다.

완료 : 비동기 작업이 성공하면 future 를 데이터 value 로 반환한다.

 

 

작성한 파일 목록 입니다.

1. pubspec.yaml

2. HttpService.dart

3. FutureScrollPage.dart

 


 

 

1. pubspec.yaml

 

http 통신을 사용하려면 일단 pubspec.yaml 파일에 dependencies 를 추가해줘야 하는데요!

https://pub.dev/packages/http

 

http | Dart Package

A composable, multi-platform, Future-based API for HTTP requests.

pub.dev

여기서 http 의 버전을 확인하여 추가해줍니다!
flutter: 와 같은 라인에 http: 를 추가해주어야해요~~!
http: ^0.13.3

=============== pubspec.yaml ===============

...

dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.3
  
...

그런데 버전을 명시하지 않고 그냥
http:   
이렇게만 입력하면 현재 최신버전을 사용하겠다는 것이라고 하네요!

=============== pubspec.yaml ===============

...

dependencies:
  flutter:
    sdk: flutter
  http: 
  
...

 

 

2. HttpService.dart

 

http 통신을 통해 데이터를 가져오는 부분입니다.

'package:http/http.dart' as http 를 import 해서 yaml 파일에 dependencies 추가한 http 를 사용하도록 해줍니다.

그리고 json 형태로 변환하기 위해 'dart:convert' 를 import 하여 json.decode() 를 사용하였습니다.

import 'dart:convert';

import 'package:flutter_application_1/model/BearItem.dart';
import 'package:http/http.dart' as http;

class HttpService {
  Future<http.Response> fetch() async {
    String url = 'http://-----:8080/MyTestProj/index.jsp';
    Uri uri = Uri.parse(url);
    return http.get(uri);
  }

  Future<BearList> fetchBear() async {
    String url = 'http://-----:8080/MyTestProj/index.jsp';
    Uri uri = Uri.parse(url);
    var response = await http.get(uri);
    var statusCode = response.statusCode;
    var headers = response.headers;
    var body = json.decode(response.body);

    print('[$runtimeType] fetchBear - statusCode : $statusCode');
    print('[$runtimeType] fetchBear - headers : $headers');
    print('[$runtimeType] fetchBear - body : $body');

    if (response.statusCode == 200) {
      return BearList.fromJson(body);
    } else {
      throw Exception(' Failed to load Bear ');
    }
  }
}

 

이 부분은 아래의 링크를 참고하여 작성하였습니다.

https://flutter.dev/docs/cookbook/networking/fetch-data

 

Fetch data from the internet

How to fetch data over the internet using the http package.

flutter.dev

 

 

3. FutureScrollPage.dart

 

기존에 dart 파일 상단에 하드코딩으로 이용했던 json 데이터를 http 통신을 통해 가져오겠습니다.

그리고 FloatingActionButton 을 통해 제일 상단/하단으로 강제 스크롤하는 코드를 사용하였습니다.


FutureBuilder 사용

 

FutureBuilder<BearList>(  //FutureBuilder 를 사용하여 데이터 가져오기
     future: HttpService().fetchBear(),  //http 통신하는 메소드
     builder: (context, snapshot) {
          if(snapshot.hasData) {  //작업이 완료 시 - snapshot 을 통해 데이터 반환
               return bodyWidget(snapshot.data!);
          } else {  //작업이 미완료시 - ProgressIndicator 위젯 표시
               return Center(
                    child: CircularProgressIndicator(),
               );
          }
     },
),

FutureBuilder 는 위의 형태를 사용합니다.


scrollController 를 사용한 강제 스크롤

 

WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {  //이 프레임에서 가장 나중에 실행되겠다
     _scrollController.animateTo(
          _scrollController.position.minScrollExtent//minScrollExtent:제일 상단, maxScrollExtent:제일 하단
          duration: Duration(milliseconds: 200),
          curve: Curves.easeInOut,
     );
});

이 형태를 사용합니다.


FutureScrollPage.dart 전체소스

import 'package:flutter/material.dart';
import 'HttpService.dart';
import 'model/BearItem.dart';

class FutureScrollPage extends StatefulWidget {
  const FutureScrollPage({
    Key? key,
  }) : super(key: key);

  @override
  _FutureScrollPageState createState() => _FutureScrollPageState();
}

class _FutureScrollPageState extends State<FutureScrollPage> {
  late ScrollController _scrollController = ScrollController();

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('FutureScrollPage'),
        ),
        floatingActionButton: Container(
          alignment: Alignment.bottomCenter,
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.end,
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              //FloatingActionButton 1 : 가장 위로 강제 스크롤
              FloatingActionButton(
                onPressed: () {
                  WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
                    _scrollController.animateTo(
                        _scrollController.position.minScrollExtent,
                        duration: Duration(milliseconds: 200),
                        curve: Curves.easeInOut,
                    );
                  });
                },
                child: Icon(Icons.arrow_upward),
              ),

              //FloatingActionButton 2 : 가장 아래로 강제 스크롤
              FloatingActionButton(
                onPressed: () {
                  WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
                    _scrollController.animateTo(
                        _scrollController.position.maxScrollExtent,
                        duration: Duration(milliseconds: 200),
                        curve: Curves.easeInOut,
                    );
                  });
                },
                child: Icon(Icons.arrow_downward),
              ),
            ],
          ),
        ),
        body: FutureBuilder<BearList>(  //FutureBuilder 를 사용하여 데이터 가져오기
          future: HttpService().fetchBear(),
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              return bodyWidget(snapshot.data!);
            } else {
              return Center(
                child: CircularProgressIndicator(),
              );
            }
          },
        ),
      ),
    );
  }

  Widget bodyWidget(BearList bearList) {
    return Container(
      child: ListView.separated(
        controller: _scrollController,
        separatorBuilder: (BuildContext context, int index) => const Divider(
          color: Colors.grey,
          height: 4,
        ), //separatorBuilder : item과 item 사이에 그려질 위젯 (개수는 itemCount -1 이 된다)
        itemCount: bearList.list!.length, //리스트의 개수
        itemBuilder: (BuildContext context, int index) {
          //리스트의 반목문 항목 형성
          return Container(
            height: 80,
            decoration: BoxDecoration(
              color: Colors.pink[index * 100],
            ),
            child: Row(
              children: [
                Container(
                  width: 50,
                  alignment: Alignment.center,
                  child: Text(
                    '$index',
                    style: TextStyle(
                      fontSize: 20,
                    ),
                  ),
                ),
                Image.asset(
                  bearList.list!.elementAt(index).image!,
                ),
                Expanded(
                  child: Container(
                    alignment: Alignment.center,
                    child: Text(
                      bearList.list!.elementAt(index).name!,
                      style: TextStyle(
                        fontSize: 20,
                      ),
                    ),
                  ),
                ),
              ],
            ),
          );
        },
      ),
    );
  }
}

 

 

 

결과화면

 

 

 

감사합니다.

 

728x90
반응형