Group Study (2023-2024)/Flutter

[Flutter] 5주차 스터디_Data Fetch와 Future Builder

팝삐 2023. 12. 21. 22:31

Fetch data from the internet

1.http 패키지 추가

flutter pub add http

- http를 사용하여 GET/POST의 방식으로 서버 API를 호출할 수 있다.

2.네트워크 요청 생성

- 메서드를 사용해서 JSONPlaceholder에서 샘플 앨범을 가져올 수 있다.

Future<http.Response> fetchAlbum() {
  return http.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));
}

- Future()

: Future 클래스의 인스턴스이다. future은 비동기 작업의 결과를 나타내며 미완료, 완료 중 한 가지 상태를 가진다. 미완료 시, 비동기 함수를 호출하면 미완료 된 future을 반환한다. 해당 future은 함수의 비동기 작업이 끝나거나 에러 발생을 기다린다. 완료 시, 비동기 작업이 성공적으로 끝나면 future은 완료되고 그렇지 않으면 에러로 완료된다.

- Uri()

: Uri 클래스는 URL에서 사용할 문자열을 인코딩하고 디코딩하는 함수를 제공한다. 이러한 함수는 URI에 특수한 문자를 처리한다. (& 혹은 =)

- parse()

: parse는 프로그램을 분석하고 런타임 환경이 실제로 실행할 수 있는 내부 형식으로 변환하는 것을 의미한다. 이 곳에서는 Json의 데이터를 텍스트 그대로 가져오는 것이 아니라 분석하고 사용 형식에 맞춰 변경해 사용할 수 있도록 하기 위해 사용했다.

3.응답을 Dart 객체로 변환

- 효율성 있는 원시 작업을 위해 http.Response의 값을 Dart 객체로 변환하는 과정이 필요하다.

class Album {
  final int userId;
  final int id;
  final String title;

  const Album({
    required this.userId,
    required this.id,
    required this.title,
  });

  factory Album.fromJson(Map<String, dynamic> json) {
    return switch (json) {
      {
        'userId': int userId,
        'id': int id,
        'title': String title,
      } =>
        Album(
          userId: userId,
          id: id,
          title: title,
        ),
      _ => throw const FormatException('Failed to load album.'),
    };
  }
}

- namedConstructor

: namedConstructor은 클래스에 대해 여러 생성자를 구현하거나 추가 명확성을 제공하기 위해 사용한다. 이름 있는 생성자를 선언하면 기본 생성자는 생략할 수 없다. 

- Map() 

: Map()은 연관된 키를 사용하여 검색하는 키/값 쌍의 컬렉션이다. 맵에는 유한한 수의 키가 있으며 각 키에는 정확히 하나의 값이 연결되어 있다. 맵과 해당 키 및 값은 반복될 수 있으며, 반복 순서는 개별 지도 유형에 따라 정의된다.

4.데이터 Fetch

class _MyAppState extends State<MyApp> {
  late Future<Album> futureAlbum;

  @override
  void initState() {
    super.initState();
    futureAlbum = fetchAlbum();
  }
  // ···
}

- initState()

: initState은 객체가 트리에 삽입될 때 정확히 한 번 호출된다. 이 객체가 트리에 삽입된 위치 또는 이 객체를 구성하는 데 사용된 위젯에 따라 초기화를 수행하기 위해 이 메서드를 재정의한다.

5.데이터 표시

- 화면에 데이터를 표시하기 위해서는 FutureBuilder 위젯을 사용해야 한다. 이 위젯은 비동기 데이터 소스로 쉽게 작업을 할 수 있게 도와준다. 이 때, 두 가지 매개 변수를 함께 제공해야 한다. 바로 ( future, builder )이다. future은 상응하는 함수를 제공해야 하고, build에는 상태에 따라 무엇을 렌더링 할 지 알려야 한다. 

FutureBuilder<Album>(
  future: futureAlbum,
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      return Text(snapshot.data!.title);
    } else if (snapshot.hasError) {
      return Text('${snapshot.error}');
    }

    // By default, show a loading spinner.
    return const CircularProgressIndicator();
  },
)

Lists & Grids

Listview

- 목록 작업을 쉽게 해주는 기본 위젯. 가장 일반적으로 사용되는 스크롤 위젯이기도 하다. 스크롤 방향으로 자식을 차례로 표시한다. ListView를 구성하는 데에는 네 가지 옵션이 있다.

1. 기본 생성자는 자식의 명시적인 List<Widget>을 사용한다. 이 생성자는 하위 수가 적은 목록을 보기에 적합하다. 목록을 구성하려면 실제로 표시되는 하위 항목만이 아니라 목록 보기에 표시될 수 있는 모든 하위 항목에 대해 작업을 수행해야 하기 때문이다. 

2. ListView.builder 생성자는 요청에 따라 하위 항목을 빌드하는 IndexedWidgetBuilder을 사용한다. 이 생성자는 실제로 표시되는 하위 항목에 대해서만 빌더가 호출되므로 하위 항목 수가 많거나 무한한 목록 보기에 적합하다. 

3. ListView.separated 생성자는 두 개의 IndexedWidgetBuilder을 사용한다. 즉, 요청 시 하위 항목을 빌드하고 유사하게 하위 항목 사이에 나타나는 구분 기호 하위를 빌드한다. 이 생성자는 고정된 수의 하위 항목이 있는 목록 보기에 적합하다.

4. ListView.custom 생성자는 하위 모델의 추가 측면을 정의하는 기능을 제공하는 SliverChildDelegate를 사용한다. 예를 들어 실제로 표시되지 않는 어린이의 크기를 추정하는 데 사용되는 알고리즘을 제어할 수 있다. 

Horizontal List

- 수직이 아닌 수평으로 스크롤되는 목록을 만들고 싶은 경우, 위젯을 통해 ListView 수평 목록을 만들 수 있다.

ListView(
  // This next line does the trick.
  scrollDirection: Axis.horizontal,
  children: <Widget>[
    Container(
      width: 160,
      color: Colors.red,
    ),
    Container(
      width: 160,
      color: Colors.blue,
    ),
    Container(
      width: 160,
      color: Colors.green,
    ),
    Container(
      width: 160,
      color: Colors.yellow,
    ),
    Container(
      width: 160,
      color: Colors.orange,
    ),
  ],
),

grid view

- GridView 위젯은 경우에 따라 항목을 차례로 표시하는 일반 항목 목록이 아닌 그리드로 항목을 표시하고 싶을 때 사용한다. 그리드 사용을 시작하는 가장 간단한 방법은 생성자를 사용하는 것이다. GridView.count() 를 통해 원하는 행과 열 수를 지정할 수 있다. 

GridView.count(
  // Create a grid with 2 columns. If you change the scrollDirection to
  // horizontal, this produces 2 rows.
  crossAxisCount: 2,
  // Generate 100 widgets that display their index in the List.
  children: List.generate(100, (index) {
    return Center(
      child: Text(
        'Item $index',
        style: Theme.of(context).textTheme.headlineSmall,
      ),
    );
  }),
),

lists with different types of items

1. 다양한 유형의 항목으로 데이터 소스 만들기

- 목록의 다양한 items의 type을 나타내려면 각 항목 유형에 대한 클래스를 정의해야 한다. 아래는 Dart에서 리스트를 생성하는 예제이다. ListItem이라는 클래스의 객체들을 담은 리스트를 생성한다. 리스트에는 1000개의 항목이 있으며, 각 항목은 HeadingItem 또는 MessageItem의 객체이다. generate 함수를 통하여 1000개의 항목을 생성하고, 조건에 따라 항목이 나눠지도록 처리한다. 

final items = List<ListItem>.generate(
  1000,
  (i) => i % 6 == 0
      ? HeadingItem('Heading $i')
      : MessageItem('Sender $i', 'Message body $i'),
);

 

2. 데이터 소스를 위젯 목록으로 변환

- ListView.builder() 생성자를 사용한다. 일반적으로 처리 중인 항목 유형을 확인하고 해당 항목 유형에 적합한 위젯을 반환하는 빌더 기능을 제공한다.

ListView.builder(
  // Let the ListView know how many items it needs to build.
  itemCount: items.length,
  // Provide a builder function. This is where the magic happens.
  // Convert each item into a widget based on the type of item it is.
  itemBuilder: (context, index) {
    final item = items[index];

    return ListTile(
      title: item.buildTitle(context),
      subtitle: item.buildSubtitle(context),
    );
  },
)

lists with spaced items

- 일반적으로 Item 사이의 공간을 넣기 위해 Spacer 또는 Expanded를 사용한다. 그러나 이러한 방법은 유한한 높이 제약이 있기 때문에 스크롤 가능한 위젯 내에서는 불가능하다.

LayoutBuilder은 공간이 충분할 때 List Items를 균등하게 사용하고 ConstrainedBox를 배치한다. 공간이 충분하지 않을 때 사용자가 스크롤할 수 있도록 허용하는 방법을 보여준다. 

SinglechildScrollView를 사용하면 상위 컨테이너가 너무 작은 경우에도 하위 위젯을 스크롤할 수 있다. 

minHeight/maxHeight을 제약 조건으로 설정하여 사용 가능한 공간, 즉 최대 높이와 동일한 최소 높이를 가지도록 제한한다. 

Column 안의 아이템들을 균등하게 유지하려면 mainAxisAlignment에 설정값을 주면된다. spaceBetween은 Column 안의 Items들에게 균등한 간격을 제공한다.

LayoutBuilder(builder: (context, constraints) {
  return SingleChildScrollView(
    child: ConstrainedBox(
      constraints: BoxConstraints(minHeight: constraints.maxHeight),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          ItemWidget(text: 'Item 1'),
          ItemWidget(text: 'Item 2'),
          ItemWidget(text: 'Item 3'),
        ],
      ),
    ),
  );
});

long lists

- ListView는 작은 목록에 적합하다. 많은 수가 포함된 목록을 작업하기 위해서는 ListView.builder 생성자를 사용하는 것이 가장 좋다. ListView.builder은 항목이 화면으로 스크롤 될 때 항목을 생성한다. 

ListView.builder(
  itemCount: items.length,
  prototypeItem: ListTile(
    title: Text(items.first),
  ),
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(items[index]),
    );
  },
)

 

 

참고문서

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

https://docs.flutter.dev/ui/layout/lists