[Flutter] 플러터(Flutter) 모바일 앱 개발 - 스팀잇 피드 구현하기

in #dclick6 years ago (edited)

안녕하세요. @anpigon 입니다.

이번에는 스팀잇 피드를 가져와서 출력하는 화면을 개발해보았습니다. 이전글 "First Flutter App"에서 추가로 학습할 부분이 거의 없어 코드 설명은 짧게 하였습니다. 그리고 핵심 로직에 집중하기 위해서 UI 컴포넌트는 최소한으로 사용합니다.


이전 글


이번에 완성된 앱의 동작화면입니다.

1

에뮬레이터 실행 화면이라서 조금 느립니다. 그리고 아직 상세 페이지를 구현하지 않아서 목록을 클릭하면 브라우저가 실행됩니다.




외부 패키지 추가

pubspec.yaml 파일을 열고 dependencies 목록에 필요한 패키지(http, url_launcher)를 추가합니다.

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.0
  http: ^0.11.3+17        # 패키지 추가
  url_launcher: ^4.0.1    # 패키지 추가




PostList와 Post 클래스 생성

스팀잇에서 Feed 를 가져와서 데이터를 담을 PostList와 Post 모델 클래스를 생성한다.

model/Post.dart

class Post {
  final num id;
  final String title;
  final String body;
  final String url;

  Post({
    this.id,
    this.title,
    this.body,
    this.url,
  });

  factory Post.fromJson(Map<String, dynamic> json) {
    return Post(
      id: json['id'] as num,
      title: json['title'] as String,
      body: json['body'] as String,
      url: json['url'] as String,
    );
  }
}


model/PostList.dart

import 'Post.dart';

class PostList {
  final List<Post> result;

  PostList({this.result});

  factory PostList.fromJson(Map<String, dynamic> json) {
    return PostList(
      result: (json['result'] as List)
          ?.map((e) => e == null ? null : new Post.fromJson(e as Map<String, dynamic>))
          ?.toList()
    );
  }
}

JSON 직렬화를 자동으로 할 수도 있다. 자세한 내용은 JSON and serialization 문서를 참고한다.




fetchFeedList 함수 구현

fetchFeedList는 Future를 async/await와 같이 사용하여 비동기 함수로 구현한다. 참고로 JavaScript(ES7)의 async/await와 동작방식은 같다.


Steemit.dart

import 'dart:async';    // 비동기
import 'dart:convert';  // JSON 파싱
import 'package:http/http.dart' as http;
import 'package:steemlog/model/PostList.dart';

class Steemit {
  /**
   * 스팀잇 피드 리스트를 조회
   */
  Future<PostList> fetchFeedList(String username, int limit) async {
    final _url = "https://api.steemit.com";

    final body = '{"jsonrpc":"2.0", "method":"condenser_api.get_discussions_by_feed", "params":[{"tag":"${username}","limit":${limit
        .toString()}}], "id":1}';
    final response = await http.post(_url, body: body);

    if (response.statusCode == 200) {
      // If server returns an OK response, parse the JSON
      return PostList.fromJson(json.decode(response.body));
    } else {
      // If that response was not OK, throw an error.
      throw Exception('Failed to load post');
    }
  }
}




화면에 출력하기

이전에 만들었던 첫번째 앱에서 _buildSuggestions()_buildRow() 함수를 수정하자. FutureBuilder의 future를 이용하여 스팀잇 피드를 비동기로 가져온다. 만약 future가 완료되지 않았다면 CircularProgressIndicator()를 사용하여 프로그래스 이미지를 보여준다.

import 'package:url_launcher/url_launcher.dart';
import 'package:steemlog/model/Post.dart';
import 'package:steemlog/model/PostList.dart';
import 'package:steemlog/Steemit.dart';

  // ... 생략 ...

class FeedListState extends State<FeedList> {
  final String _username = 'anpigon'; // 사용자 이름
  final int _limit = 20; // 가져올 피드 개수

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text('Feed List'),
        ),
        body: _buildSuggestions()
    );
  }

  Widget _buildSuggestions() {
    return new Center(
        child: FutureBuilder<PostList>(
            future: Steemit().fetchFeedList(_username, _limit),
            builder: (context, post) {
              if (post.hasData) {
                return new ListView.builder(
                  padding: const EdgeInsets.all(5.0),
                  itemCount: post.data.result.length,
                  itemBuilder: (BuildContext _context, int position) {
                    return _buildRow(post.data.result[position]);
                  },
                );
              } else if (post.hasError) {
                return Text("${post.error}");
              }
              return CircularProgressIndicator();
            }
        )
    );
  }

  Widget _buildRow(Post post) {
    return new Column(
      children: <Widget>[
        new ListTile(
          title: new Text(post.title),
          subtitle: new Text(post.body,
            overflow: TextOverflow.ellipsis,
            maxLines: 3
          ),
          onTap: () {
            _launchURL(post.url);
          },
        ),
        const Divider()
      ]
    );
  }

  _launchURL(String _url) async {
    final String url = "https://steemit.com${_url}";
    if (await canLaunch(url)) {
      await launch(url);
    } else {
      throw 'Could not launch $url';
    }
  }
}

Text에서 제공하는 overflow 속성을 사용하여 내용을 최대 3줄만 표시되도록 했다.




여기서 작성한 코드는 모두 깃허브에 올려놓았습니다.


여기까지 읽어주셔서 감사합니다.



Sponsored ( Powered by dclick )
드디어 이지 스팀잇이 출간되었습니다(+M-shape도요~)

안녕하세요. 포해피우먼입니다. 오늘은 반가운 소식을 가지고 왔습니다. 아기다리고기다리 (-_-...

logo

이 글은 스팀 기반 광고 플랫폼
dclick 에 의해 작성 되었습니다.

Sort:  

Fabulous. So magnificent.

Thank you. Thanks to your comments, I am very excited.

오오오오.
스팀잇 앱하나 만드시는건가요!!!??ㅎㅎㅎ

플러터를 공부하다보니 스팀잇앱이 만들고 싶어졌어요. 하지만 아직은 공부를 더 해야할 것 같습니다. ㅋ

blockchainstudio님이 anpigon님을 멘션하셨습니당. 아래 링크를 누르시면 연결되용~ ^^
blockchainstudio님의 태그 - 알고보니 한글뿐만아니라 이모티콘, 대문자, 마침표, 매우 긴 태그 등 다 가능. 무법천지

감사합니다. 근데 파티코에선 한글태그 인식이 잘되나요? 저랑 anpigon 테스트해봤었을때는 api로 직접작성했을시에 5개초과여부와 관계없이 인식이 제멋대로더라고요. 되기도 하고 안되기도 하고. 한글과 관계없이 원래 태그로 글가...

언어는 무엇인가요? 코드를 보니 생소해 보이네요.

구글에서 만든 다트(Dart) 언어입니다. 자바와 자바스크립트의 모습을 갖추고 있습니다.ㅎ

@anpigon님 곰돌이 자다 깨서 보팅 왔어요. 그럼 전 다시 꿈나라로~ @gomdory 곰도뤼~

고마워요. 곰도뤼~