ハンズオンで学ぶFlutter(データ表示編)

#復習
以前の記事で、Flutterでの状態管理の方法を学びました。
state_notifierとfreezedというパッケージを活用したのですが、今回は、そこで管理されている状態を実際に見た目の部分に表示していきます。

##Apiから持ってきたデータを表示する
それでは、先ほど格納したデータを実際の画面に表示していきます。
news_controller.dartで作成したものを使用するための準備をしていきます。
###Widgetの最上部にproviderを設定する。

widgetの最上部は、main.dart付近になります。

```dart:lib/main.dart

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    //news_controllerファイルにあるproviderを使うために、Widgetの最上部辺りに、MultiProviderを置き、StateNotifierProviderをセットする。
    return MultiProvider(
      providers: [
        StateNotifierProvider<NewsController, NewsState>(
          create: (context) => NewsController(),
        )
      ],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        //viewフォルダのmain_page.dartに書かれているコードが入る
        home: MainPage(),
      ),
    );
  }
}

```

MultiProviderというWidgetがあると思いますが、その中で、providerの欄で自分たちが作成したproviderを設定することで、NewsStateとNewsControllerで作ったデータやロジックをどこのwidgetでも使うことが出来ます。
(今回は一つのproviderしかセットしていないためMultiProviderをわざわざセットする必要はありません。)

###Providerを用いて、データを表示する。

MyAppクラスのhome内を見ていきましょう

```dart:main.dart

class MainPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //_fetchにNwesController内のgetNwesメソッドを格納
    final _fetch =
        Provider.of<NewsController>(context, listen: false).getNews();
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: Text("Demo"),
      ),
      //非同期処理の処理が完了した後にwidgetを生成するwidget
      body: FutureBuilder<void>(
        //Widget_fetchをfuture: に設定
        future: _fetch,
        builder: (context, snapshot) {
          if (snapshot.connectionState != ConnectionState.done) {
            return Center(child: CircularProgressIndicator());
          }
          if (snapshot.hasError) {
            print("エラーが出たよ");
          }
          return HeadNewsListPage();
        },
      ),
    );
  }
}
```

home内にMainPageクラスを作成しています。

####どのようにProviderで設定したデータやメソッドを使うか。
まず、重要になる部分が、
`Provider.of<NewsController>(context, listen: false).getNews();`
の部分です。
以前作成したNwesStateとNwesControllerのデータと、ロジックを使うためには、
`Provider.of<T>()`という形を作ることでデータや、メソッドを使用できます。

NwesController内のgetNwesメソッドを使用したい場合、上のように
`Provider.of<NewsController>(context, listen: false).getNews();`
と書くことでメソッドを使用できます。
この場合、contextを渡す必要があります。また、listen内でfalseかtrueかを選べるのですが、Widgetを再描画したい場合はtrue、したくない場合はfalseにします。
getNwesメソッドの場合は、falseにしておきます。
NwesStateに格納されているデータをしようしたい場合は、
`Provider.of<NewsState>(context, listen: true).articles`
というように書いて使用します。

####FutureBuilder
ここで、bodyの部分でFutureBuilderというWidgetを使用していると思います。
これは、`何らかの非同期処理を待ってから、その非同期処理の結果を受けWidgetを作成するWidget`になります。

ニュースデータを非同期処理でapiからとってきているので、すこし時間がかかります。そんな時にこのFutureBuilderを活用します。
futureの部分に行いたい処理を記述します。

その下は、api取ってきちゅうだったらぐるぐるを回して、データがとってこれたらHeadListNwesPageWidgetを作成していきます。

####データを表示
HeasNwesListPageはニュース一覧を表示するWidgetです。
[ハンズオンで学ぶFlutter(UI構築編)](https://qiita.com/syo2255/items/a578611632f0dc7e4d40)で書いたと思います。
現在は、データを入れていないので、ここにデータを挿入し表示させていきます。

```dart

class HeadNewsListPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //NwesStateのデータをstateに入れる。
    final state = Provider.of<NewsState>(context, listen: true);
    return Padding(
      padding: const EdgeInsets.only(top: 10),
      child: Container(
        margin: EdgeInsets.symmetric(horizontal: 10),
        //リストを作成するwidget(今回は,itemcountを10個で、ArticleTileのリストを作成)
        child: ListView.builder(
          //stateの中のarticleにデータがリスト形式で入っているので、itemCountにいくつのデータが入っているか示すために、state.article.lengthで数を出す。
          itemCount: state.articles.length,
          itemBuilder: (context, index) {
            //itemBuilderは引数にindexを持っている。このindexを使うことで、一つ一つのニュースを挿入する。
            return ArticleTile(state.articles[index]);
          },
        ),
      ),
    );
  }
}

class ArticleTile extends StatelessWidget {
  final Article article;

  ArticleTile(this.article);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 6),
      child: Card(
        color: Colors.grey,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(8.0),
        ),
        //RowはWidgetを横に並べることのできるwidget
        child: Row(
          children: [
            //ニュース画像を表示するエリア
            Expanded(
                flex: 2,
                child: Container(
                  //画像はurlで帰ってきているので、それを画像として表示するには、Image.networkWidgetを使い、引数にURLを挿入する。
                  child: Image.network(
                    article.urlToImage,
                    fit: BoxFit.fill,
                  ),
                  // color: Colors.grey,
                )),
            Expanded(
                //ニュース説明を表示するエリア
                flex: 5,
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Container(
                    child: Column(
                      children: [
                        Text(
                          //ニュースの名前を表示する。
                          article.title,
                          style: TextStyle(fontWeight: FontWeight.bold),
                        ),
                        //ニュースが発行された日を記述する。
                        Text(article.publishedAt),
                      ],
                    ),
                    // color: Colors.amber,
                  ),
                )),
          ],
        ),
      ),
    );
  }
}
```

前に記載したように、NwesStateのデータを使いたい場合は、
`Provider.of<NwesState>(context, listen: true)`
で使用できます。
ここでは、上のものを`state`に入れているので、articlesを使用したい場合は、state.articlesとすることで、リスト形式でニュースデータを使用することが出来ます。

これで、一度ビルドしてみてください。

###最後に
今回は、ハンズオンでUI構築、状態管理を行ってきました。
注意していただきたいのが、今回のコードだけではニュースを表示するところまでできません。
もし、すべてのコードがほいいという方は、
git clone https://github.com/shokiogawa/hands_on.git

からコードをクローンしてください。
lib/networking/api_service.dartのファイルの中に、自分で取得したapiキーが必要になります。