9Cells

Networking

http 패키지와 shared_preferences 패키지를 설치합니다. <latest_version> 은 패키지 문서를 확인해주세요.

dependencies:
  http: <latest_version>
  shared_preferences: <latest_version>

콘솔에서 실행합니다.

$ flutter pub get

import 합니다.

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

Auth token을 얻고 API 호출

initState()에서 SharedPreferences에서 token을 찾아보고 없으면 token을 받아서 저장합니다. token을 사용하여 API를 호출합니다.

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

  _loadQuizzes() async {
    final prefs = await SharedPreferences.getInstance();
    String authToken = prefs.getString('token') ?? null;
    if (authToken == null) {
      String token = await getAuthToken();
      authToken = token;
      prefs.setString('token', token);
    }
    _quizzes = getQuizzes(authToken);
    setState(() {});
  }

API 구현 예제:

Future<List<Quiz>> getQuizzes(token) async {
  final response = await http.get(
    'https://myquiz.test/quizzes',
    headers: {
      "Content-type": "application/json",
      "Accept": "application/json",
      "Authorization": "Bearer $token",
    },
  );

  if (response.statusCode == 200) {
    List responseList = json.decode(response.body);
    return responseList.map((data) => Quiz.fromJson(data)).toList();
  } else {
    throw Exception('Failed to load quizzes');
  }
}

HTTP status code를 확인하는 부분을 다음처럼 수정하면 201 Created 같은 상태 코드에서 예외가 나는 것을 피할 수 있습니다.

if (response.statusCode >= 200 && response.statusCode < 300) {
}

FutureBuilder를 사용하여 API 호출 후 결과를 받으면 화면을 다시 만들도록 구현.

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Go back!")),
      body: FutureBuilder<List<Quiz>>(
        future: _quizzes,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            if (snapshot.hasData) {
              return ListView.builder(
                // ...
              );
            } else if (snapshot.hasError) {
              return Text("${snapshot.error}");
            }
          }
          return Center(
            child: CircularProgressIndicator(),
          );
        },
      ),
    );
  }

API 호출결과 hasError가 true면 다른 작업이 필요할 수 있습니다. 아래의 예제는 API 호출 후 권한이 없다는 에러 발생 시, Login 화면으로 이동하는 코드입니다.

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Go back!")),
      body: FutureBuilder<List<Quiz>>(
        future: _quizzes,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            if (snapshot.hasError) {
              // 401에러 발생 시 LoginView로 이동
              SchedulerBinding.instance.addPostFrameCallback((_) {
                Navigator.pushReplacement(
                  context,
                  PageRouteBuilder(
                    pageBuilder: (context, animation1, animation2) =>
                        LoginView(),
                  ),
                );
              });
              return Container();
            }

여기서 주의할 점은 API 호출결과를 build() 안에서 처리하고 있기 때문에, build()가 끝나기 전에 네비게이션을 바꾸면 에러가 발생한다는 점입니다. 이런 코드를 작성해야 하는 경우 SchedulerBinding.instance.addPostFrameCallback() 을 사용하여 네비게이션 변경 작업을 지연시키면 됩니다.