Riverpod + Http

송기영·2024년 2월 6일
0

플러터

목록 보기
25/25

플러터 공부하던중 Header에 TextField가 있고 검색을 하면 연관 검색어가 나오도록 구현을 하고 싶었다. 구현완료 했으나 귀찮아서 나중에 작성하겠습니다...

부모에서 자식에게 내려주는 방식을 생각하다가 글로벌 상태로 처리하면 되겠다는 생각이 들었고 막연하게 바로 구현을 하는것보다는 먼저 restful api의 get요청을 통해 데이터를 가져와 표현해주는 작업부터 진행하였고 작업의 결과물을 정리하고자 한다.

글로벌 상태는 provider, riverpod, bloc가 있는데 요즘 가장 핫한 riverpod로 선택하였다.

최종 결과물

최종적으로 아래와 같은 화면을 표시합니다.

폴더 구조

root 
 ├─lib
 │  ├─ dataprovider
 │  │  └─ data_provider.dart
 │  ├─ home
 │  │  └─ my_home_page.dart	
 │  ├─ models
 │  │  └─ user_model.dart	
 │  ├─ services
 │  │  └─ services.dart	
 │  └─ main.dart
 └─ pubspec.yaml

데이터 구조

표현하고자 하는 데이터는 아래와 같은 형식의 유저 데이터이다. 자세한 데이터 내용은 여기에서 확인하면 됩니다.

{
	"page":2,
	"per_page":6,
	"total":12,
	"total_pages":2,
	"data":[
		{
			"id":7,
			"email":"michael.lawson@reqres.in",
			"first_name":"Michael",
			"last_name":"Lawson",
			"avatar":"https://reqres.in/img/faces/7-image.jpg"
		}
	],
	"support":{
		"url":"https://reqres.in/#support-heading",
		"text":"To keep ReqRes free, contributions towards server costs are appreciated!"
	}
}

라이브러리 설치

글로벌 상태관리 라이브러리 riverpod과 restful api 통신을 위한 라이브러리 http를 설치해준다.

  • pubspce.yaml
dependencies:
  cupertino_icons: ^1.0.6
  flutter:
    sdk: flutter
  flutter_riverpod: ^2.4.10
  http: ^1.2.0

유저 모델 생성

유저의 모델로 사용될 UserModel 클래스를 만들어준다. UserModel.fromJson을 통해 필요한 데이터만 뽑아오는 작업을 합니다.

  • user_model.dart
class UserModel {
  final int id;
  final String email;
  final String firstname;
  final String lastname;
  final String avatar;

  const UserModel(
      {required this.id,
      required this.email,
      required this.firstname,
      required this.lastname,
      required this.avatar});

  factory UserModel.fromJson(Map<String, dynamic> json) {
    return UserModel(
        id: json["id"],
        email: json["email"],
        firstname: json["first_name"],
        lastname: json["last_name"],
        avatar: json["avatar"]);
  }
}

서비스 생성

http의 get요청을 통해 데이터를 가져오는 서비스를 생성합니다.

  • services.dart
import 'dart:convert';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart';
import 'package:riverpod_http/models/user_model.dart';

class ApiServices {
  String endpoint = "https://reqres.in/api/users?page=2";
  Future<List<UserModel>> getUsers() async {
    Response response = await get(Uri.parse(endpoint));
    if (response.statusCode == 200) {
      final List result = jsonDecode(response.body)['data'];
      return result.map((e) => UserModel.fromJson(e)).toList();
    } else {
      throw Exception(response.reasonPhrase);
    }
  }
}

데이터 프로바이더 생성

가져온 데이터를 실제로 저장하는 데이터 프로바이더를 생성합니다.

  • data_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_http/models/user_model.dart';
import 'package:riverpod_http/services/services.dart';

final userDataProvider = FutureProvider<List<UserModel>>((ref) async {
  return ApiServices().getUsers();
});

페이지 생성

가져온 데이터를 표시하기 위해 데이터를 표시합니다.

여기서 AsyncValue 타입을 사용하게 되면 data, error, loading 상태에 대해서 모두 처리할 수 있기 때문에 AsyncValue 타입을 사용했습니다.

  • my_home_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_http/dataprovider/data_provider.dart';
import 'package:riverpod_http/models/user_model.dart';
import 'package:riverpod_http/screens/detail_screen.dart';

class MyHomePage extends ConsumerWidget {
  const MyHomePage({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
		// ref.watch를 통해 userDataProvider 감지
    AsyncValue<List<UserModel>> _data = ref.watch(userDataProvider);
    return Scaffold(
      appBar: AppBar(
        title: const Text("RiverPod"),
      ),
      body: _data.when(
          data: (_data) {
            List<UserModel> userList = _data.map((e) => e).toList();
            return Padding(
              padding: const EdgeInsets.symmetric(horizontal: 10.0),
              child: Column(
                children: [
                  Expanded(
                      child: ListView.builder(
                          itemCount: userList.length,
                          itemBuilder: (_, index) {
                            return Card(
                              color: Colors.blue,
                              elevation: 4,
                              margin: const EdgeInsets.symmetric(vertical: 10),
                              child: ListTile(
                                title: Text(
                                  userList[index].firstname,
                                  style: const TextStyle(color: Colors.white),
                                ),
                                subtitle: Text(
                                  userList[index].lastname,
                                  style: const TextStyle(color: Colors.white),
                                ),
                                trailing: CircleAvatar(
                                  backgroundImage:
                                      NetworkImage(userList[index].avatar),
                                ),
                              ),
                            );
                          }))
                ],
              ),
            );
          },
					// error일때 표시
          error: (err, s) => Text(err.toString()),
					// loading일때 표시
          loading: () => const Center(
                child: CircularProgressIndicator(),
              )),
    );
  }
}

설명이 좀 빈약한데 자세한 내용은 아래 동영상을 참고하면 됩니다.

레퍼런스

https://www.youtube.com/watch?v=2EV5w73QbF4&ab_channel=dbestech

profile
업무하면서 쌓인 노하우를 정리하는 블로그🚀 풀스택 개발자를 지향하고 있습니다👻

0개의 댓글