虎視眈々と

Flutter × Firebaseを研究するアプリエンジニア

FlutterのBLoCアーキテクチャを実装する

f:id:superman199323:20181208124641p:plain

FlutterのBLoCアーキテクチャを実装する

Flutterで有名なBLoCアーキテクチャの実装方法について紹介します。

BLoCとは

BLoCとはGoogleが推奨しているアーキテクチャになります。 詳しくは下記をご覧ください

www.shogogeek.com

こちらもすごく参考になるきじなのでこちらもどうぞ

ntaoo.hatenablog.com

今回はこれに発展してAPIを叩くような場合の実装をします。

構成

構成はこんな感じです。

f:id:superman199323:20181208125842p:plain

パッケージ構成はこうします。

f:id:superman199323:20181208130013p:plain

実装

それではソースコードを貼っていきます。

準備とUIScreenを実装します。

  • main.dart
import 'package:flutter/material.dart';
import 'package:flutter_app_bloc_sample_app/src/app.dart';

void main() => runApp(App());
  • app.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app_bloc_sample_app/src/ui/scenery_list.dart';

class App extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark(),
      home: Scaffold(
        body: SceneryList(),
      ),
    );
  }
}

UI Screen

  • ui/scenery_list.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app_bloc_sample_app/src/blocs/scenery_bloc.dart';
import 'package:flutter_app_bloc_sample_app/src/models/image_model.dart';

class SceneryList extends StatelessWidget {
  final _bloc = SceneryBloc();

  @override
  Widget build(BuildContext context) {
    _bloc.fetchAllScenery();
    return Scaffold(
      appBar: AppBar(
        title: Text("景色画像一覧"),
      ),
      body: StreamBuilder(
          stream: _bloc.allScenery,
          builder: (_, snapshot) {
            if (snapshot.hasData) {
              return _buildList(snapshot);
            } else if (snapshot.hasError) {

              return Text("エラーが発生しました" + snapshot.error.toString());
            }

            return Center(
              child: CircularProgressIndicator(),
            );
          }),
    );
  }

  Widget _buildList(AsyncSnapshot<List<ImageModel>> snapshot) {
    return GridView.builder(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2),
      itemBuilder: (_, index) {
        ImageModel model = snapshot.data[index];
        return Image.network(model.imageUrl);
      },
      itemCount: snapshot.data.length,
    );
  }
}

BLOCの部分です。

  • scenery_bloc.dart
import 'package:rxdart/rxdart.dart';
import 'package:flutter_app_bloc_sample_app/src/models/image_model.dart';
import 'package:flutter_app_bloc_sample_app/src/resources/repository.dart';

class SceneryBloc {
  final _repository = Repository();
  final _sceneryFetcher = PublishSubject<List<ImageModel>>();

  Observable<List<ImageModel>> get allScenery => _sceneryFetcher.stream;

  fetchAllScenery() async {
    List<ImageModel> imageModelList = await _repository.fetchAllProvider();
    _sceneryFetcher.sink.add(imageModelList);
  }

  dispose() {
    _sceneryFetcher.close();
  }
}

次にRepository

import 'package:flutter_app_bloc_sample_app/src/models/image_model.dart';

import 'scenery_image_provider.dart';

class Repository {
  final provider = new SceneryImageProvider();

  Future<List<ImageModel>> fetchAllProvider() => provider.fetchImageList();
}

次にNetwork Provider

import 'package:flutter_app_bloc_sample_app/src/models/image_model.dart';
import 'package:http/http.dart' show Client;
import 'dart:convert';


class SceneryImageProvider {
  Client client = Client();

  Future<List<ImageModel>> fetchImageList() async {
    // Json用意しておいたのでこちらを使ってみれください。
    final response = await client.get(
        "https://firebasestorage.googleapis.com/v0/b/blog-1a47d.appspot.com/o/json%2Fdata.json?alt=media&token=e67da5e7-b8d4-4000-9dc3-394e6a5d1549");

    print(response.body);
    if (response.statusCode == 200) {
      // 成功
      List<dynamic> jsonArray = JsonDecoder().convert(response.body);
      return jsonArray.map((i) => ImageModel(i)).toList();
    } else {
      // 失敗
      throw Exception('Failed to load post');
    }
  }
}

これで完成です。

ソースコードはこちらにおきました

github.com