Slide 1

Slide 1 text

Android΍iOSͷΞϓϦ։ൃͰ ͍ͭ΋ࢲ͕΍͍ͬͯΔ͜ͱΛ FlutterͰ΍ͬͯΈͨ 2018/09/29 GDGਆށ Flutterษڧձ

Slide 2

Slide 2 text

• Տຊ ହ޹(͔Θ΋ͱ ΍͔ͨ͢) • ॴଐɿגࣜձࣾ tech vein 
 (େࡕࢢதԝ۠ຊொ) • ϞόΠϧΞϓϦΤϯδχΞ • GitHub: kwmt ɺtwitter: kwmt27 • Google I/O2018 ॳࢀՃ 2 ࣗݾ঺հ

Slide 3

Slide 3 text

3 ΞϓϦ։ൃͰ͍ͭ΋΍͍ͬͯΔ͜ͱ 1. ઃܭ͸Clean Architecture 2. ։ൃͱຊ൪Ͱઃఆ੾ସ 3. CI/CD

Slide 4

Slide 4 text

1. Clean Architecture͸FlutterͰ࣮ݱͰ͖Δ͔ʁ

Slide 5

Slide 5 text

5 Clean Architecture https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html

Slide 6

Slide 6 text

• ϑϨʔϜϫʔΫͱಠཱ • ςετ͠΍͍͢ • UIͱಠཱ • σʔλϕʔεͱಠཱ • جຊతʹͲΜͳιϑτ΢ΣΞ(ΞϓϦͰ΋αʔόʔͰ΋)Ͱ ΋࠾༻Ͱ͖ΔͷͰɺͲ͜ʹԿ͕͋Δ͔͕౷Ұग़དྷͯ෼͔ Γ΍͍͢ Clean ArchitectureͷϝϦοτ 6

Slide 7

Slide 7 text

Clean Architecture Screen

Slide 8

Slide 8 text

Clean Architecture Screen

Slide 9

Slide 9 text

Clean Architecture Use Case Screen

Slide 10

Slide 10 text

Clean Architecture Use Case Repository Screen

Slide 11

Slide 11 text

Clean Architecture Use Case Repository Screen Data Source

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

• ΠϯλʔϑΣΠε͕͋Δ͜ͱ • DI͕Ͱ͖Δ͜ͱ Clean ArchitectureΛ࣮ݱ͢ΔͨΊʹ͸ 8

Slide 17

Slide 17 text

• ͋Δ • interfaceͱ͍͏ΩʔϫʔυͷΠϯλʔϑΣΠε͸ͳ͍ • classΛఆٛ͢Δͱಉ໊ͷΠϯλʔϑΣΠε͕҉໧తʹ ఆٛ͞ΕΔ • ந৅ϝιουͷΈΛ΋ͭjavaͷΑ͏ͳΠϯλʔϑΣΠε ͷΑ͏ʹ͢Δʹ͸ɺabstractΩʔϫʔυΛ࢖ͬͯந৅ ΫϥεΛ࡞Δ DartʹΠϯλʔϑΣΠε͕͋Δ͔ʁ abstract class Person { void walk(); } 9

Slide 18

Slide 18 text

• DIͱ͸ɺඞཁͳ΋ͷΛ֎͔Β౉͢͜ͱɻ • ґଘؔ܎Λ؆୯ʹղܾͯ͘͠ΕΔDIϓϥάΠϯ͸͋Δ ͔ʁ • flutter_simple_dependency_injection DIͰ͖Δ͔ʁ https://pub.dartlang.org/packages/flutter_simple_dependency_injection 10

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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> fetchRoomList(User user) async { QuerySnapshot querySnapshot = await _firestore .collection('rooms') .getDocuments(); return _fetchRoomListWithMembers(querySnapshot); } } Presentation Domain Infrastructure

Slide 27

Slide 27 text

DIͰͭͳ͙ class FirestoreDatasource implements RoomRepository { final Firestore _firestore; FirestoreDatasource(this._firestore); } 15

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

DIͰͭͳ͙ class DependencyInjection { void init() { // লུ injector.mapWithParams((i, p) => RoomListScreen( p["user"], i.get() )); } } 17

Slide 31

Slide 31 text

RoomListScreenͰUseCaseΛ࢖͏ class RoomListScreen extends StatefulWidget final User _user; final FetchRoomListUseCase _fetchRoomListUseCase; RoomListScreen( this._user, this._fetchRoomListUseCase, ) @override _RoomListScreen createState() => _RoomListScreen(); } 18

Slide 32

Slide 32 text

RoomListScreenͰUseCaseΛ࢖͏ class _RoomListScreen extends State { List _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

Slide 33

Slide 33 text

2. ։ൃͱຊ൪ͷઃఆΛ੾Γସ͑Δʹ͸ʁ

Slide 34

Slide 34 text

։ൃɾຊ൪؀ڥΛ੾Γସ͑Δʹ͸ • Android΍iOSͷ͍ͭ΋ͷઃఆ͕ඞཁ • Android:flavorͷઃఆͳͲ • iOS: Schemeઃఆ΍։ൃ༻ͷinfo.plist࡞੒ͳͲ • ։ൃ͸development, ຊ൪͸productionͱ໊લΛ෇͚ͨ ͱ͠·͢ɻ 21

Slide 35

Slide 35 text

InheritedWidget class https://docs.flutter.io/flutter/widgets/InheritedWidget-class.html • WidgetπϦʔͷԼҐπϦʔͷWidgetʹޮ཰తʹ఻͑Δ ϕʔεΫϥε • context͔ΒInheritedWidgetͷΠϯελϯεΛऔಘ͢Δʹ ͸ɺBuildContext.inheritFromWidgetOfExactTypeΛ࢖͏ • BuildContext.inheritFromWidgetOfExactTypeΛ࢖ͬͯࢀ র͞ΕͨInheritedWidget͸ɺWidgetͷঢ়ଶ͕มΘͬͨͱ ͖ɺ࠶ߏஙͤ͞Δ͜ͱ͕Ͱ͖Δ 22

Slide 36

Slide 36 text

InheritedWidget class Λ࢖ͬͨྫ APP Root Login Home auth auth auth 23

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

InheritedWidget class Λ࢖ͬͨྫ 24 APP Login Root Widget A Widget B Home auth auth auth auth auth

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

InheritedWidget class Λ࢖ͬͨྫ APP Auth Provider Root Login Home auth InheritedWidgetΛܧঝ 26

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

mainϑΝΠϧΛ։ൃɾຊ൪ʹ෼͚Δ // main.dart void main() { runApp(MyApp()); } 28

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

AppConfigΛ࢖͏ AppConfig.of(context).baseWebUrl 30

Slide 51

Slide 51 text

࣮ߦ࣌΍Ϗϧυ࣌ʹ։ൃ͔ຊ൪Λࢦఆ͢Δʹ͸ $ flutter run -t lib/main_development.dart --flavor development 31

Slide 52

Slide 52 text

Android StudioͰ੾Γସ͑Δʹ͸ 32

Slide 53

Slide 53 text

Android StudioͰ੾Γସ͑Δʹ͸ 33

Slide 54

Slide 54 text

Android StudioͰ੾Γସ͑Δʹ͸ 34

Slide 55

Slide 55 text

Android StudioͰ੾Γସ͑Δʹ͸ 34

Slide 56

Slide 56 text

3. CI/CD͸Ͳ͏͢Δͷ͕ྑͦ͞͏͔ʁ

Slide 57

Slide 57 text

36 CI/CD CI αʔϏε Git αʔϏε

Slide 58

Slide 58 text

36 CI/CD CI αʔϏε Git αʔϏε

Slide 59

Slide 59 text

36 CI/CD CI αʔϏε Git αʔϏε

Slide 60

Slide 60 text

36 CI/CD CI αʔϏε Git αʔϏε

Slide 61

Slide 61 text

36 CI/CD CI αʔϏε Git αʔϏε

Slide 62

Slide 62 text

GitαʔϏε Public Private GitHub ແྉ ༗ྉ GitLab ແྉ ແྉ 37

Slide 63

Slide 63 text

GitαʔϏε Public Private GitHub ແྉ ༗ྉ GitLab ແྉ ແྉ 37

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

CIαʔϏεxGitαʔϏε GitHub GitLab circleci ରԠ ඇରԠ Bitrise ରԠ ରԠ Cirrus CI ରԠ ඇରԠ 39

Slide 67

Slide 67 text

CIαʔϏεxGitαʔϏε GitHub GitLab circleci ରԠ ඇରԠ Bitrise ରԠ ରԠ Cirrus CI ରԠ ඇରԠ 39

Slide 68

Slide 68 text

40 ࠓճͷΞϓϦ։ൃͰͷ࢖༻αʔϏε CI αʔϏε Git αʔϏε

Slide 69

Slide 69 text

40 ࠓճͷΞϓϦ։ൃͰͷ࢖༻αʔϏε CI αʔϏε

Slide 70

Slide 70 text

40 ࠓճͷΞϓϦ։ൃͰͷ࢖༻αʔϏε

Slide 71

Slide 71 text

41 Bitrise 10෼/1Ϗϧυ(ແྉϓϥϯ)

Slide 72

Slide 72 text

42

Slide 73

Slide 73 text

42

Slide 74

Slide 74 text

43 Fastlane https://flutter.io/fastlane-cd/

Slide 75

Slide 75 text

44 Bitrise+DeployGate https://qiita.com/kyoro353/items/200d5b34b9f5805dd43a

Slide 76

Slide 76 text

45 ·ͱΊ • ΫϦʔϯΞʔΩςΫνϟͰઃܭ͸Ͱ͖Δ • ։ൃͱຊ൪Ͱઃఆͷ੾ସՄೳ • CI/CD͸࢖͏αʔϏεͷ૊Έ߹ΘͤʹΑͬͯ͸ແྉͰ࣮ ݱͰ͖ͦ͏Ͱ͕͢ɺprivateϦϙδτϦͷ৔߹͸ݱঢ়ແ ྉͰͷ࣮ݱ͸ݫ͍͠

Slide 77

Slide 77 text

Happy Fluttering!