Upgrade to Pro — share decks privately, control downloads, hide ads and more …

AndroidやiOSのアプリ開発でいつも私がやっていることをFlutterでやってみた / Flutter development as usual

AndroidやiOSのアプリ開発でいつも私がやっていることをFlutterでやってみた / Flutter development as usual

GDG神戸 Flutter勉強会(9/29)での登壇資料です
https://gdgkobe.doorkeeper.jp/events/78737

## キーワード
- クリーンアーキテクチャ
- 設定の開発と本番の切り替え
- CI/CD

## ソースコード
https://github.com/kwmt/flutter-inconne

## 参考
- Clean architecture https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html
- DIについてわかりやすいと思う記事 https://blog.ishkawa.org/2017/09/18/1505701774/
- flavorについて https://medium.com/@salvatoregiordanoo/flavoring-flutter-392aaa875f36

Yasutaka Kawamoto

September 29, 2018
Tweet

More Decks by Yasutaka Kawamoto

Other Decks in Programming

Transcript

  1. • Տຊ ହ޹(͔Θ΋ͱ ΍͔ͨ͢) • ॴଐɿגࣜձࣾ tech vein 
 (େࡕࢢதԝ۠ຊொ)

    • ϞόΠϧΞϓϦΤϯδχΞ • GitHub: kwmt ɺtwitter: kwmt27 • Google I/O2018 ॳࢀՃ 2 ࣗݾ঺հ
  2. • ϑϨʔϜϫʔΫͱಠཱ • ςετ͠΍͍͢ • UIͱಠཱ • σʔλϕʔεͱಠཱ • جຊతʹͲΜͳιϑτ΢ΣΞ(ΞϓϦͰ΋αʔόʔͰ΋)Ͱ

    ΋࠾༻Ͱ͖ΔͷͰɺͲ͜ʹԿ͕͋Δ͔͕౷Ұग़དྷͯ෼͔ Γ΍͍͢ Clean ArchitectureͷϝϦοτ 6
  3. Clean Architecture Presentation Use Case Model Repository Screen Data Source

    Entity ੺ɿґଘؔ܎ ੨ɿσʔλͷྲྀΕ Domain Infrastructure
  4. Clean Architecture Presentation Use Case Model Repository Screen Data Source

    Entity ੺ɿґଘؔ܎ ੨ɿσʔλͷྲྀΕ Domain Infrastructure
  5. 11 Clean Architecture - Domain - Model Use Case Model

    Repository Screen Data Source Entity ੺ɿґଘؔ܎ ੨ɿσʔλͷྲྀΕ Presentation Domain Infrastructure
  6. 11 Clean Architecture - Domain - Model Use Case Model

    Repository Screen Data Source Entity ੺ɿґଘؔ܎ ੨ɿσʔλͷྲྀΕ class Room { String id; String name; String photoUrl; } class User { String id; String name; String photoUrl; } Presentation Domain Infrastructure
  7. Presentation Domain Infrastructure 12 Clean Architecture - Domain - Use

    Case Use Case Model Repository Screen Data Source Entity ੺ɿґଘؔ܎ ੨ɿσʔλͷྲྀΕ
  8. Presentation Domain Infrastructure 12 Clean Architecture - Domain - Use

    Case Use Case Model Repository Screen Data Source Entity ੺ɿґଘؔ܎ ੨ɿσʔλͷྲྀΕ /// ϢʔβʔͷRoomϦετΛऔಘ͢Δ abstract class FetchRoomListUseCase { Future<List<Room>> execute(User user); }
  9. Presentation Domain Infrastructure 12 Clean Architecture - Domain - Use

    Case Use Case Model Repository Screen Data Source Entity ੺ɿґଘؔ܎ ੨ɿσʔλͷྲྀΕ /// ϢʔβʔͷRoomϦετΛऔಘ͢Δ abstract class FetchRoomListUseCase { Future<List<Room>> execute(User user); } class FetchRoomListUseCaseImpl implements FetchRoomListUseCase { final RoomRepository _roomRepository; FetchRoomListUseCaseImpl(this._roomRepository); @override Future<List<Room>> execute(User user) { return _roomRepository.fetchRoomList(user); } }
  10. 13 Clean Architecture - Domain - Repository Use Case Model

    Repository Screen Data Source Entity ੺ɿґଘؔ܎ ੨ɿσʔλͷྲྀΕ abstract class RoomRepository { Future<List<Room>> fetchRoomList(User user); } Presentation Domain Infrastructure
  11. 14 Clean Architecture - Infrastructure - Data Source Use Case

    Model Repository Screen Data Source Entity ੺ɿґଘؔ܎ ੨ɿσʔλͷྲྀΕ Presentation Domain Infrastructure
  12. 14 Clean Architecture - Infrastructure - Data Source Use Case

    Model Repository Screen Data Source Entity ੺ɿґଘؔ܎ ੨ɿσʔλͷྲྀΕ class FirestoreDatasource implements RoomRepository { final Firestore _firestore; FirestoreDatasource(this._firestore); @override Future<List<Room>> fetchRoomList(User user) async { QuerySnapshot querySnapshot = await _firestore .collection('rooms') .getDocuments(); return _fetchRoomListWithMembers(querySnapshot); } } Presentation Domain Infrastructure
  13. DIͰͭͳ͙ class DependencyInjection { void init() { // DIϓϥάΠϯ͔ΒinjectorΛऔಘ final

    injector = Injector.getInjector(); // औಘͨ͠injectorʹରͯ͠FirestoreΠϯελϯεΛੜ੒͠ηοτ͢Δ injector.map<Firestore>((i) => Firestore(), isSingleton: true); // FirestoreDatasourceͷίϯετϥΫλͷҾ਺ʹ // FirestoreΠϯελϯεΛ౉͢ injector.map<FirestoreDatasource>( (i) => FirestoreDatasource(i.get<Firestore>()), isSingleton: true); } } class FirestoreDatasource implements RoomRepository { final Firestore _firestore; FirestoreDatasource(this._firestore); } 15
  14. DIͰͭͳ͙ class DependencyInjection { void init() { // লུ injector.map<RoomRepository>((i)

    => i.get<FirestoreDatasource>(), isSingleton: true); injector.map<FetchRoomListUseCase>((i) => FetchRoomListUseCaseImpl(i.get<RoomRepository>()), isSingleton: true); } } 16
  15. DIͰͭͳ͙ class DependencyInjection { void init() { // লུ injector.mapWithParams<RoomListScreen>((i,

    p) => RoomListScreen( p["user"], i.get<FetchRoomListUseCase>() )); } } 17
  16. RoomListScreenͰUseCaseΛ࢖͏ class RoomListScreen extends StatefulWidget final User _user; final FetchRoomListUseCase

    _fetchRoomListUseCase; RoomListScreen( this._user, this._fetchRoomListUseCase, ) @override _RoomListScreen createState() => _RoomListScreen(); } 18
  17. RoomListScreenͰUseCaseΛ࢖͏ class _RoomListScreen extends State<RoomListScreen> { List<Room> _rooms = List();

    bool _isProgress = false; @override void initState() { super.initState(); _isProgress = true; widget._fetchRoomListUseCase.execute(widget._user).then((rooms) { setState(() { this._rooms = rooms; _isProgress = false; }); }); } 19
  18. InheritedWidget class Λ࢖ͬͨྫ APP Root Login Home auth auth auth

    Root(auth:Auth()) ະϩάΠϯͳΒ Login(auth: auth) ϩάΠϯࡁΈͳΒ Home(auth: auth) 23
  19. InheritedWidget class Λ࢖ͬͨྫ WidgetB(auth: auth) Login(auth: auth) WidgetA(auth: auth) 24

    APP Login Root Widget A Widget B Home auth auth auth auth auth
  20. InheritedWidget class Λ࢖ͬͨྫ class AuthProvider extends InheritedWidget { final Auth

    auth; AuthProvider({this.auth, Widget child}) : super(child: child); static AuthProvider of(BuildContext context) { return context.inheritFromWidgetOfExactType(AuthProvider); } @override bool updateShouldNotify(AuthProvider oldWidget) => true; } 25
  21. InheritedWidget class Λ࢖ͬͨྫ APP Auth Provider Root Login Home auth

    InheritedWidgetΛܧঝ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return AuthProvider( auth: Auth(), child: App(); } } 26
  22. InheritedWidget class Λ࢖ͬͨྫ APP Auth Provider Root Login Home auth

    AuthProvider.of(context).auth AuthProvider.of(context).auth AuthProvider.of(context).auth InheritedWidgetΛܧঝ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return AuthProvider( auth: Auth(), child: App(); } } 26
  23. AppConfig class AppConfig extends InheritedWidget { AppConfig({this.baseWebUrl, Widget child}) :

    super(child: child); static AppConfig of(BuildContext context) { return context.inheritFromWidgetOfExactType(AppConfig); } @override bool updateShouldNotify(AppConfig oldWidget) => false; final String baseWebUrl; String get privacyPolicyUrl => this.baseWebUrl + "/privacy.html"; } 27
  24. mainϑΝΠϧΛ։ൃɾຊ൪ʹ෼͚Δ // main.dart void main() { runApp(MyApp()); } // main_development.dart

    void main() { runApp(MyApp()); } // main_production.dart void main() { runApp(MyApp()); } 28
  25. mainϑΝΠϧΛ։ൃɾຊ൪ʹ෼͚Δ // main_development.dart void main() { var appConfig = AppConfig(

    baseWebUrl: “https://development.com”, child: MyApp() ); runApp(appConfig); } 29
  26. mainϑΝΠϧΛ։ൃɾຊ൪ʹ෼͚Δ // main_development.dart void main() { var appConfig = AppConfig(

    baseWebUrl: “https://development.com”, child: MyApp() ); runApp(appConfig); } // main_production.dart void main() { var appConfig = AppConfig( baseWebUrl: “https://production.com”, child: MyApp() ); runApp(appConfig); } 29
  27. CIαʔϏε Android iOS උߟ circlecI ແྉ ༗ྉ Bitrise ແྉ ແྉ

    10෼/1Ϗϧυ×200ճ/݄ Cirrus CI ແྉ ແྉ publicϦϙδτϦͷΈ privateϦϙδτϦ͸༗ྉ 38
  28. CIαʔϏε Android iOS උߟ circlecI ແྉ ༗ྉ Bitrise ແྉ ແྉ

    10෼/1Ϗϧυ×200ճ/݄ Cirrus CI ແྉ ແྉ publicϦϙδτϦͷΈ privateϦϙδτϦ͸༗ྉ 38
  29. 42

  30. 42