shinonome-inc / mobile_Koshi

2 stars 0 forks source link

FeedPage #2

Open mcz9mm opened 3 years ago

mcz9mm commented 3 years ago
Yoshida-koshi commented 3 years ago

`import 'package:flutter/material.dart'; import 'dart:convert'; import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/cupertino.dart'; import 'package:http/http.dart' as http; import 'package:webview_flutter/webview_flutter.dart';

class User { final String id; final String iconUrl; User({ required this.id, required this.iconUrl, }); factory User.fromJson(Map<String, dynamic> json) { return User( id: json['id'], iconUrl: json['profile_image_url'], ); } }

class Article { final String title; final String url; final User user;

Article({ required this.title, required this.url, required this.user, }); factory Article.fromJson(Map<String, dynamic> json) { return Article( title: json['title'], url: json['url'], user: User.fromJson(json['user']), ); } }

class QiitaClient { static Future<List

> fetchArticle() async { final url = 'https://qiita.com/api/v2/items' as Uri; final response = await http.get(url); if (response.statusCode == 200) { final List jsonArray = json.decode(response.body); return jsonArray.map((json) => Article.fromJson(json)).toList(); } else { throw Exception('Failed to load article'); } } }

class ArticleListView extends StatelessWidget { final List

articles;

ArticleListView({ Key? key, required this.articles, }) : super(key: key);

@override Widget build(BuildContext context) { return ListView.builder( itemCount: articles.length, itemBuilder: (BuildContext context, int index) { final article = articles[index]; return ListTile( leading: CircleAvatar( backgroundImage: NetworkImage(article.user.iconUrl), ), title: Text(article.title), onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ArticleDetailPage(article: article)), ); }); }, ); } }

class ArticleDetailPage extends StatelessWidget { final Article article;

ArticleDetailPage({ Key? key, required this.article, }) : super(key: key);

@override Widget build(BuildContext context) { return MaterialApp( title: 'Fetch Data Example', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: Center( child: WebView( initialUrl: article.url, ), ), ), ); } } class FeedPage extends StatelessWidget { final Future<List

> articles = QiitaClient.fetchArticle();

@override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( toolbarHeight: 130, backgroundColor: Colors.white, title: Column( children: [ Text('Feed', style: TextStyle( color: Colors.black, fontSize: 17, fontFamily: 'Pacifico', ), ), Container( padding: EdgeInsets.fromLTRB(0, 19, 0, 0), child: SizedBox( height: 36, child: TextField( decoration: InputDecoration( border: OutlineInputBorder(), labelText: 'search', prefixIcon: Icon(Icons.search), ), ), ), ), ], ), centerTitle: true, ), body: Center( child: FutureBuilder<List

>( future: articles, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { return ArticleListView(articles: snapshot.data); } return CircularProgressIndicator(); } ), ), ), ); } }` 上記のコードを実行すると、以下の写真のようにずっとCircularProgressIndicator()の部分が実行され、記事一覧が表示できません。 2021-08-07 また以下のサイトに沿って記事一覧表示を試みました。 https://qiita.com/abouch/items/90107b330bb126a6f742 何か教えて頂けるとありがたいです。

mcz9mm commented 3 years ago

ここのテキストはマークダウンでコードを埋め込めるので見やすいように修正してもらっても良いですか? https://docs.github.com/ja/github/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks

mcz9mm commented 3 years ago

まず、どこの部分が問題か原因調査を行なってください。

考えられる要因はたくさんありますので、各要所でprintを仕込んでデータが正しく取得できているか確認したり、 ブレークポイントを張ってチェックしてみてください。

Yoshida-koshi commented 3 years ago
import 'package:flutter/material.dart';
import 'dart:convert';
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/cupertino.dart';
import 'package:http/http.dart' as http;
import 'package:webview_flutter/webview_flutter.dart';

class User {
  final String id;
  final String iconUrl;
  User({
    required this.id,
    required this.iconUrl,
});
  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'],
      iconUrl: json['profile_image_url'],
    );
  }
}

class Article {
  final String title;
  final String url;
  final User user;

  Article({
    required this.title,
    required this.url,
    required this.user,
});
  factory Article.fromJson(Map<String, dynamic> json) {
    return Article(
      title: json['title'],
      url: json['url'],
      user: User.fromJson(json['user']),
    );
  }
}

class QiitaClient {
  static Future<List<Article>> fetchArticle() async {
    final url = 'https://qiita.com/api/v2/items' as Uri;
    final response = await http.get(url);
    if (response.statusCode == 200) {
      final List<dynamic> jsonArray = json.decode(response.body);
      return jsonArray.map((json) => Article.fromJson(json)).toList();
    } else {
      throw Exception('Failed to load article');
    }
  }
}

class ArticleListView extends StatelessWidget {
  final List<Article> articles;

  ArticleListView({
    Key? key,
    required this.articles,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: articles.length,
      itemBuilder: (BuildContext context, int index) {
        final article = articles[index];
        return ListTile(
            leading: CircleAvatar(
              backgroundImage: NetworkImage(article.user.iconUrl),
            ),
            title: Text(article.title),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                    builder: (context) => ArticleDetailPage(article: article)),
              );
            });
      },
    );
  }
}

class ArticleDetailPage extends StatelessWidget {
  final Article article;

  ArticleDetailPage({
    Key? key,
    required this.article,
}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fetch Data Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        body: Center(
          child: WebView(
            initialUrl: article.url,
          ),
        ),
      ),
    );
  }
}
class FeedPage extends StatelessWidget {
  final Future<List<Article>> articles = QiitaClient.fetchArticle();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          toolbarHeight: 130,
          backgroundColor: Colors.white,
          title: Column(
            children: [
              Text('Feed',
                style: TextStyle(
                  color: Colors.black,
                  fontSize: 17,
                  fontFamily: 'Pacifico',
                ),
              ),
              Container(
                padding: EdgeInsets.fromLTRB(0, 19, 0, 0),
                child: SizedBox(
                  height: 36,
                  child: TextField(
                    decoration: InputDecoration(
                      border: OutlineInputBorder(),
                      labelText: 'search',
                      prefixIcon: Icon(Icons.search),
                    ),
                  ),
                ),
              ),
            ],
          ),
          centerTitle: true,
        ),
        body: Center(
          child: FutureBuilder<List<Article>>(
            future: articles,
            builder: (BuildContext context, AsyncSnapshot snapshot) {
              if (snapshot.hasData) {
                return ArticleListView(articles: snapshot.data);
              }
              return CircularProgressIndicator();
            }
          ),
        ),
      ),
    );
  }
}
mcz9mm commented 3 years ago

原因調査進んでますか? 実行時のログなどにもエラーなど出力されている可能性があるのでチェックしてみると良いかもしれません

mcz9mm commented 3 years ago

どこが上手くいってなさそうとか、デバッグするなりして調査してみるとかしてますか? 自分が考えたことを一言添えることをチャレンジしてみてください。

Yoshida-koshi commented 3 years ago
Future<List<Item>> getItemList({int page = 1, QiitaItemsQuery? query}) async {
    final accessToken = await getAccessToken();
    String url = 'https://qiita.com/api/v2/items?page=$page';
    if (query != null) {
      url += '&query=${query.buildString()}';
    }

    final response = await http.get(
      Uri.parse(url),
      headers: {
        "content-type": "application/json",
        "Authorization": "Bearer $accessToken",
      }
    );

    Map<String, Item> body = Map<String, Item>.from(jsonDecode(response.body));
    final itemList = (body as List<dynamic>).map((item) {
      return Item(
        title: item['title'],
        url: item['url'],
        user: item['user'],
        likesCount: item['likes_count'],
        createdAt: item['created_at'],
      );
    }).toList();

    return itemList;
  }
}

class QiitaItemsQuery {
  String? userID;

  QiitaItemsQuery userIdEquals(String id) {
    userID = id;
    return this;
  }

  String buildString() {
    List<String> queries = [];

    if (userID != null) {
      queries.add('user:$userID');
    }
    return queries.join('');
  }
}

上記のコードを打つと以下の写真のようなエラーがでてきてしまいます。 Screenshot_1629367736 以下のサイトを参考に直すことを試みましたが、全く実装できませんでした。 https://stackoverflow.com/questions/52276158/file-is-not-a-subtype-of-type-string-in-type-cast 何が間違っているのか教えて頂けるとありがたいです。

mcz9mm commented 3 years ago

decodeに失敗してるんじゃないかな? いきなりresponseのbodyをそのままMap<String, Item>に変換しようとしてて型が合わないってエラー出てると思う。

Map<String, Item> body = Map<String, Item>.from(jsonDecode(response.body));

一度bodyをdynamic(型を指定しない書き方)にして、レスポンスのjsonの中身がarrayであるならばmapで取り出して、それをItemにdecodeしてあげるとうまくいくと思う。

final List<dynamic> jsonArray = json.decode(response.body);
final itemList = jsonArray.map((item) {
      return Item(
        title: item['title'],
        url: item['url'],
        user: item['user'],
        likesCount: item['likes_count'],
        createdAt: item['created_at'],
      );
    }).toList();

https://flutter.dev/docs/development/data-and-backend/json

Yoshida-koshi commented 3 years ago
final List<dynamic> jsonArray = json.decode(response.body);
final itemList = jsonArray.map((item) {
      return Item(
        title: item['title'],
        url: item['url'],
        user: item['user'],
        likesCount: item['likes_count'],
        createdAt: item['created_at'],
      );
    }).toList();

上記のように直したのですが、以下の写真のようなエラーがでてしまいます。 Screenshot_1629452526 何が間違えているのか教えて頂けるとありがたいです。

mcz9mm commented 3 years ago

printやブレークポイントを張って調査した結果を教えてください。

Yoshida-koshi commented 3 years ago
factory Item.fromJson(Map<String, dynamic> json) {
   return Item(
    title: json['title'],
    createdAt: json['created_at'],
    likesCount: json['likes_count'],
    user: User.fromJson(json['user']),
    url: json['url'],
);
}

とする必要があるところを

factory Item.fromJson(Map<String, dynamic> json) {
   return Item(
      title: json['title'],
      createdAt: json['created_at'],
      likesCount: json['likes_count'],
      user: json['user'],
      url: json['url'],
);
}

としてしまったので先ほどのエラーになってしまいました。 教えて頂きありがとうございました。