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

実例と歴史から学ぶ_Flutterの状態管理方法の選定_.pdf

4bd69b0ec8e5731a5332ee100ad8fb6d?s=47 entaku
November 28, 2021

 実例と歴史から学ぶ_Flutterの状態管理方法の選定_.pdf

4bd69b0ec8e5731a5332ee100ad8fb6d?s=128

entaku

November 28, 2021
Tweet

Transcript

  1. ࣮ྫͱྺ࢙͔ΒֶͿʂ Flutterͷঢ়ଶ؅ཧํ๏ͷબఆ FlutterKaigi 2021 11/29 19:45ʙ entaku / @entaku_0818

  2. • Name: ԕ౻୓໻ (entaku) • Twitter: @entaku_0818 • Job: ϞόΠϧΞϓϦΤϯδχΞ

    • Android / Flutter / iOSͷܦݧ͋Γ • iOSΤϯδχΞͷܦݧ͕௕͍ ࣗݾ঺հ
  3. Flutterྺ 2019/4ϦϦʔε 2021/4ϦϦʔε(ϦϓϨΠε)

  4. 2019/4ϦϦʔε 2021/4ϦϦʔε(ϦϓϨΠε) StatefulWidget Provider Flutterྺ

  5. Flutterͷঢ়ଶ؅ཧԿ࢖͑͹͍͍͔Θ͔Βͳ͍ 😥

  6. iOSͷ࣌ࣗ෼͸ͲͷΑ͏ʹֶΜ͚ͩͬʁ 🤔

  7. https://peaks.cc/books/iOS_architecture

  8. https://peaks.cc/books/iOS_architecture ͳͥͦͷઃܭ͕࠾༻͞Ε͍ͯΔͷ͔ʁ Λ࣮ྫͱྺ࢙ͷ؍఺͔Βॻ͔Ε͍ͯΔ

  9. ࠓճ๻΋͜ͷΑ͏ͳࢿྉΛ࡞Γ͍ͨʂ 🔥

  10. Flutterঢ়ଶ؅ཧͷྺ࢙ͷ೉͠͞ •Flutter ϦϦʔε౰ॳ(2018/12)͔Β3೥ऑ͔͠ܦͬͯͳ͍ •ެࣜͰఏࣔ͞Ε͍ͯΔख๏͸͋Δ΋ͷͷ؅ཧख๏͸ͨ͘͞Μ͋Δ •10ݸҎ্ͷํ๏͕ఏࣔ͞Ε͍ͯΔ https://docs. fl utter.dev/development/data-and-backend/state-mgmt

  11. Flutterঢ়ଶ؅ཧͷͨ͘͞Μͷํ๏ StatefulWidget Provider Riverpod InheritedWidget ScopedModel Redux Fish-Redux BLoC /

    Rx GetIt https://docs. fl utter.dev/development/data-and-backend/state-mgmt
  12. Flutterঢ়ଶ؅ཧͷͨ͘͞Μͷํ๏ StatefulWidget Provider Riverpod InheritedWidget ScopedModel Redux Fish-Redux BLoC /

    Rx GetIt https://docs. fl utter.dev/development/data-and-backend/state-mgmt ͷதͰ΋ಛʹຊےͷྲྀΕ͕෼͔Γͦ͏ͳͱ͜ΖΛϐοΫΞοϓ
  13. Ͱ͸࣮ࡍʹͦΕͧΕͷίʔυΛݟ͍͖ͯ·͠ΐ͏

  14. ͦͷલʹ…

  15. Flutter is declarative

  16. https:// fl utter.dev/docs/development/data-and-backend/state-mgmt/declarative Flutter is declarative =Flutter͸એݴతUIͰ͢ Flutter is declarative

    is licensed under Creative Commons Attribution 4.0 International License,
  17. Sample CodeͷΠϝʔδ Football API APIΛୟ͘ औಘͨ͠σʔλΛҰཡͰදࣔ https://github.com/entaku0818/football_ fl utter

  18. Ͱ͸࣮ࡍʹͦΕͧΕͷίʔυΛݟ͍͖ͯ·͠ΐ͏

  19. StatefulWidget

  20. StatefulWidget •Flutter ϦϦʔε౰ॳ(2018/12)͔Β͋Δ •WidgetʹState͕ηοτʹͳ͍ͬͯΔ https://api. fl utter.dev/ fl utter/widgets/StatefulWidget-class.html

  21. https://api. fl utter.dev/ fl utter/widgets/StatelessWidget-class.html https://api. fl utter.dev/ fl utter/widgets/StatefulWidget-class.html

    class GreenFrog extends StatelessWidget { const GreenFrog({ Key? key }) : super(key: key) ; @override Widget build(BuildContext context) { return Container(color: const Color(0xFF2DBD3A)) ; } } class YellowBird extends StatefulWidget { const YellowBird({ Key? key }) : super(key: key) ; @override State<YellowBird> createState() => _YellowBirdState() ; } class _YellowBirdState extends State<YellowBird> { @override Widget build(BuildContext context) { return Container(color: const Color(0xFFFFE306)) ; } } StatefulWidget - ެࣜͷࣄྫ
  22. class HomeScreen extends StatefulWidget { HomeScreen({Key key}) : super(key: key)

    ; ɹ// StateΛࢦఆ _HomeScreenState createState() => _HomeScreenState() ; } class _HomeScreenState extends State<HomeScreen> { ɹ ɹ// νʔϜҰཡΛఆ͍ٛͯ͠Δ List<Team> _teams = <Team>[] ; void initState() { // StateͷॳظԽॲཧ super.initState() ; fetch(); // ը໘ΞΫηε࣌ʹσʔλΛऔಘͯ͘͠Δ } StatefulWidget - SampleCode
  23. @overrid e Widget build(BuildContext context) { return Scaffold ( appBar:

    AppBar ( title: const Text('νʔϜҰཡ') , ) , body: ListView.builder ( itemBuilder: (BuildContext context, int index) { return Card ( child: ListTile ( title: Text(_teams[index].name) , ) , ) ; } , itemCount: _teams.length , ) , ) ; } Future<List<Team>> fetch() async { final _teamsRepository = TeamsRepository() ; final result = await _teamsRepository.feachTeams() ; result.when ( success: (teams) { setState(() { _teams = teams ; }) ; } , failure: (error) { // Τϥʔॲཧ } , ) ; } StatefulWidget - SampleCode
  24. ϝϦοτ σϝϦοτ w σʔλΛ7JFXʹ࣋ͨͳ͚Ε͹ͳΒͳ͍ w ࣄྫͰ͸νʔϜҰཡͷσʔλΛ8JEHFUͱಉ͡৔ॴʹఆٛ w ͋ΒΏΔॲཧ͕7JFXʹॻ͔ΕΔͷͰ'BU7JFXʹͳΔ w ࣄྫͰ͸νʔϜҰཡͷσʔλऔಘॲཧΛ8JEHFUͱಉ͡৔ॴʹఆٛ

    w Ұը໘Ͱॲཧ͕ॻ͚ΔͷͰ෼͔Γ΍͍͢ StatefulWidget
  25. ScopedModel

  26. ScopedModel •2018/11 ʹversion 1 •࠷ۙ͸ߋ৽͞Ε͍ͯͳ͍ •WidgetͱModelͷ෼཭ https://pub.dev/packages/scoped_model https://pub.dev/packages/scoped_model/example

  27. // Note: It must extend from Model. class CounterModel extends

    Model { int _counter = 0 ; int get counter => _counter ; void increment() { // First, increment the counter _counter++ ; // Then notify all the listeners. notifyListeners() ; } } ScopedModel - ެࣜͷࣄྫ ঢ়ଶΛ΋ͭ.PEFMΛఆٛ
  28. class CounterApp extends StatelessWidget { @override Widget build(BuildContext context) {

    // First, create a `ScopedModel` widget. This will provide // the `model` to the children that request it. return new ScopedModel<CounterModel> ( model: new CounterModel() , child: new Column(children: [ // Create a ScopedModelDescendant. This widget will get the // CounterModel from the nearest ScopedModel<CounterModel>. // It will hand that model to our builder method, and rebuild // any time the CounterModel changes (i.e. after we // `notifyListeners` in the Model). new ScopedModelDescendant<CounterModel> ( builder: (context, child, model) => new Text('${model.counter}') , ) , new Text("Another widget that doesn't depend on the CounterModel" ) ] ) ) ; } } ScopedModel - ެࣜͷࣄྫ ར༻͢Δ 4DPQFE.PEFMΛఆٛ .PEFMΛར༻͍ͨ͠8JEHFUΛ 4DPQFE.PEFM%FTDFOEBOUͰ แΉ
  29. class ScopedModelHome extends StatelessWidget { @overrid e Widget build(BuildContext context)

    { // StatelessWidgetͰఆٛ͠ModelΛݺͼग़͢=> ViewʹϏδωεϩδοΫ͕ೖͬͯ͜ͳ͍ return ScopedModel<TeamScopedModel> ( model: TeamScopedModel() , child: ScopedModelDescendant<TeamScopedModel> ( // ScopedModelDescendant഑ԼͰϞσϧΛࢀর builder: (context, child, model) => Scaffold ( appBar: AppBar ( title: const Text('νʔϜҰཡ') , ) , drawer: Header() , body: ListView.builder ( itemBuilder: (BuildContext context, int index) { return Card ( child: ListTile ( title: Text(model.teams[index].name) , ) , ) ; } , itemCount: model.teams.length , ) , ) , ) , ) ; } } ScopedModel - SampleCode
  30. class TeamScopedModel extends Model { List<Team> _teams = [] ;

    List<Team> get teams => _teams ; TeamScopedModel() { // ॳճ࣮ߦ࣌ʹσʔλऔಘΛ࣮ߦ fetch() ; } Future<List<Team>> fetch() async { final _teamsRepository = TeamsRepository() ; final result = await _teamsRepository.feachTeams() ; result.when ( success: (teams) { _teams = teams ; // σʔλߋ৽࣌ʹΠϕϯτൃՐ notifyListeners() ; } , failure: (error) { // Τϥʔॲཧ } , ) ; } } ScopedModel - SampleCode
  31. ϝϦοτ σϝϦοτ w ࠷ޙͷߋ৽͕ w ·ͩ/VMM4BGFUZʹ΋ରԠ͍ͯ͠ͳ͍ w ࠓޙߋ৽͞ΕΔՄೳੑ͸௿͍ʁ w w

    4UBUFGVM8JEHFUͱൺֱͯ͠ϏδωεϩδοΫΛ෼཭͠΍͍͢ ScopedModel - SampleCode
  32. Provider

  33. https://pub.dev/packages/provider •2018/10 ver1.0.0 •͜ͷ࣌ظʹ࢖ͬͯͨਓ͍Δͷ͔ͳ.. •ެࣜαΠτͰਪ঑͞Ε͍ͯΔ΍Γํ https://docs. fl utter.dev/development/data-and-backend/state-mgmt/simple https://pub.dev/packages/provider Provider

  34. void main() { runApp ( ChangeNoti fi erProvider ( create:

    (context) => CartModel(), // ChangeNoti fi e r child: const MyApp(), // Widge t ) , ) ; } Provider - ެࣜͷࣄྫ $IBOHF/PUJ fi FS1SPWJEFS Ͱ$IBOHF/PUJ fi FSͱ8JEHFUΛ ఆٛ
  35. class CartModel extends ChangeNoti fi er { /// Internal, private

    state of the cart. fi nal List<Item> _items = [] ; /// An unmodi fi able view of the items in the cart. Unmodi fi ableListView<Item> get items => Unmodi fi ableListView(_items) ; /// The current total price of all items (assuming all items cost $42). int get totalPrice => _items.length * 42 ; /// Adds [item] to cart. This and [removeAll] are the only ways to modify the /// cart from the outside. void add(Item item) { _items.add(item) ; // This call tells the widgets that are listening to this model to rebuild. notifyListeners() ; } /// Removes all items from the cart. void removeAll() { _items.clear() ; // This call tells the widgets that are listening to this model to rebuild. notifyListeners() ; } } Provider - ެࣜͷࣄྫ $IBOHF/PUJ fi FSͰ஋Λఆٛ
  36. return Consumer<CartModel> ( builder: (context, cart, child) { return Text("Total

    price: ${cart.totalPrice}") ; } , ); Provider - ެࣜͷࣄྫ $POTVNFSͰғ͏͜ͱͰ $IBOHF/PUJ fi FSൃՐ࣌ʹ 8JEHFUΛߋ৽
  37. Provider - SampleCode '/changeNotifier': (BuildContext context) => ChangeNotifierProvider ( create:

    (context) => TeamModel(),ɹ// ChangeNotifie r child: ChangeNotifierHome(), // Widge t ) ,
  38. Provider - SampleCode class ChangeNotifierHome extends StatelessWidget { @overrid e

    Widget build(BuildContext context) { return Scaffold ( appBar: AppBar ( title: const Text('νʔϜҰཡ') , ) , drawer: Header() , // Widget಺ͷߋ৽ൣғΛConsumerͰғ͏ body: Consumer<TeamModel> ( builder: (BuildContext context, TeamModel value, Widget child) { return ListView.builder ( itemBuilder: (BuildContext context, int index) { return Card ( child: ListTile ( title: Text(value.teams[index].name) , ) , ) ; } , itemCount: value.teams.length , ) ; })) ; } }
  39. Provider - SampleCode class TeamModel extends ChangeNotifier { List<Team> _teams

    = [] ; List<Team> get teams => _teams ; TeamModel() { // ॳظԽ࣌ʹσʔλऔಘ fetch() ; } Future<List<Team>> fetch() async { final _teamsRepository = TeamsRepository() ; final result = await _teamsRepository.feachTeams() ; result.when ( success: (teams) { _teams = teams ; //ɹมߋΛ௨஌ notifyListeners() ; } , failure: (error) { //ɹΤϥʔॲཧ } , ) ; } }
  40. Provider w ෳ਺ͷ1SPWJEFSͱ8JEHFUͷ૊Έ߹ΘͤΛར༻͢Δ৔߹ ը໘਺͕ଟ͍ෳࡶͳը ໘ભҠ ʹΤϥʔʹͳͬͯ͠·͏ ϝϦοτ σϝϦοτ w 1SPWJEFSͱ8JEHFUͷ૊Έ߹Θͤґଘੑͷ஫ೖͱঢ়ଶ؅ཧ͕ߦ͑Δ

    '/changeNotifier': (BuildContext context) => ChangeNotifierProvider ( create: (context) => TeamModel() , child: ChangeNotifierHome() , ) ,
  41. Riverpod

  42. https://pub.dev/packages/provider •2021/11/5ʹ ver1.0.0 •Providerͷ໰୊఺Λղফ͢ΔܗͰ஀ੜ https://pub.dev/packages/riverpod https://riverpod.dev RiverPod

  43. fi nal counterProvider = StateNoti fi erProvider((ref) { return Counter()

    ; }) ; class Counter extends StateNoti fi er<int> { Counter(): super(0) ; void increment() => state++ ; } Riverpod - ެࣜͷࣄྫ άϩʔόϧʹ1SPWJEFSΛఆٛ
  44. class Example extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef

    ref) { fi nal count = ref.watch(counterProvider) ; return Text(count.toString()) ; } } Riverpod - ެࣜͷࣄྫ $POTVNFS8JEHFUͰ8JEHFUΛੜ੒ ͠ɺ SFGXBUDIͰ1SPWJEFSͷঢ়ଶΛ؅ཧ
  45. Riverpod // ProviderScopeͰର৅ͷWidgetΛғ͏ '/RiverpodHome': (BuildContext context) => ProviderScope ( child:

    RiverpodHome() , )
  46. Riverpod // TeamStateNotifierΛࢀর͠ɺάϩʔόϧʹެ։ final TeamStateNotifierProvider = StateNotifierProvider<TeamStateNotifier, List<Team>> ( (ref)

    => TeamStateNotifier([]) , ) ; class TeamStateNotifier extends StateNotifier<List<Team>> { TeamStateNotifier(List<Team> state) : super(state) { // ॳظԽ࣌ʹσʔλऔಘ _fetch() ; } Future<List<Team>> _fetch() async { final _teamsRepository = TeamsRepository() ; final result = await _teamsRepository.feachTeams() ; result.when ( success: (teams) { state = teams ; } , failure: (error) { // Τϥʔॲཧ } , ) ; } }
  47. Riverpod class RiverpodHome extends ConsumerWidget { @overrid e Widget build(BuildContext

    context, WidgetRef ref) { // ref.watchͰProviderͷঢ়ଶΛࢀর final teams = ref.watch(TeamStateNotifierProvider) ; return Scaffold ( appBar: AppBar ( title: const Text('νʔϜҰཡ') , ) , drawer: Header() , body: ListView.builder ( itemBuilder: (BuildContext context, int index) { return Card ( child: ListTile ( title: Text(teams[index].name) , ) , ) ; } , itemCount: teams.length , ) , ) ; } }
  48. Riverpod ϝϦοτ σϝϦοτ w 1SPWJEFSΛάϩʔόϧͰఆٛՄೳͰͲͷ8JEHFU͔Β΋࢖͑Δ w ແཧ໼ཧ͋͛ΔͳΒɺ ϦϦʔεͨ͠͹͔ΓͰมߋͷՄೳੑ͕͋Δ w 1SPWJEFS͸WFSTJPO·Ͱग़͍ͯΔ

  49. • ·ͣγϯϓϧͳΞϓϦΛ࡞ΔͳΒStatefulWidget͕͍͍ • ܦݧऀ΋ଟ͍ͷͰ࣮ྫ΋͋Δ͸ͣ • ScopedModel͸ߋ৽͞Εͯͳ͍Α͏ͩ͠ࠓޙ͸࢖͏΂͖Ͱ͸ͳ ͦ͞͏ • ෳ਺ը໘Ͱঢ়ଶΛڞ༗͢Δ৔߹͸Provider΍RiverpodΛ࢖༻͢Δ •

    ҆ఆੑ͕ཉ͍͠ͳΒProvider • ը໘ભҠ͕ෳࡶͳΒRiverpod ·ͱΊ
  50. ΈΜͳ͸Կ࢖͍ͬͯΔΜͩΖ͏ʁ 🤔

  51. ΞϯέʔτͱͬͯΈ·ͨ͠ʂ 11/19࣌఺ 19ճ౴ https://docs.google.com/forms/d/e/ 1FAIpQLSccB2iZJ78FeLPj1ST31FVUxj73Tc4d-0KhXpbJnT-l9kYZMQ/viewform

  52. ݩʑωΠςΟϒ։ൃऀ͕໿7ׂ Flutter͔Β͸3ׂ

  53. શͯͷਓ͕StatefulWidgetΛ࢖ͬͯͨ BLoC΍Redux͸ͦΜͳʹ࢖ΘΕ͍ͯͳ͍ʁ

  54. StatefulWidget͸ গͣͭ͠࢖ΘΕͳ͘ͳ͖͍ͬͯͯΔ Riverpod͕த৺͔ʁ

  55. ࠓޙ͸RiverpodΛ࢖͍͖͍ͬͯͨਓ͕ଟ਺

  56. ࣮ྫͱྺ࢙͔ΒֶͿʂ Flutterͷঢ়ଶ؅ཧํ๏ͷ બఆ

  57. ࣮ྫͱྺ࢙͔ΒֶͿʂ Flutterͷঢ়ଶ؅ཧํ๏ͷ બఆ ʴະདྷ

  58. https://speakerdeck.com/aomathwift/ji-neng-gotonidong-zuo- suruminiapuridepurebiyusaikuruwobao-su-nisitahua ઌ೔ͷiOSDCͰ͋ͬͨϛχΞϓϦͷ࿩ https://speakerdeck.com/aomathwift/ji-neng-gotonidong-zuo- suruminiapuridepurebiyusaikuruwobao-su-nisitahua

  59. ઌ೔ͷiOSDCͰ͋ͬͨϛχΞϓϦͷ࿩ https://techlife.cookpad.com/entry/2021/06/16/110000

  60. কདྷతʹ StatefulWidget γϯϓϧͳΞϓϦΛ࡞Δ (ը໘಺Ͱ׬݁) Provider ෳࡶͳΞϓϦΛ࡞Γ͍ͨ ଟ͘ͷը໘ભҠ͕͋Δ Riverpod ? ϚϧνϞδϡʔϧ

    Խͯ͠ϛχΞϓϦ Λ࡞Δ
  61. Ξϯέʔτͷ݁Ռ͔Β΋ࠓޙ΋ͬͱ ΋ͬͱFlutterͰ࡞ΔΞϓϦͷෳࡶ͞͸ ૿͍ͯ͘͠ͷͰ͸ʁ🤔

  62. ϗϯτͷ·ͱΊ • ·ͣγϯϓϧͳΞϓϦΛ࡞ΔͳΒStatefulWidget͕͍͍ • ܦݧऀ΋ଟ͍ͷͰ࣮ྫ΋͋Δ͸ͣ • ScopedModel͸ߋ৽͞Εͯͳ͍Α͏ͩ͠ࠓޙ͸࢖͏΂͖Ͱ͸ͳ ͦ͞͏ • ෳ਺ը໘Ͱঢ়ଶΛڞ༗͢Δ৔߹͸Provider΍RiverpodΛ࢖༻͢Δ

    • ҆ఆੑ͕ཉ͍͠ͳΒProvider • ը໘ભҠ͕ෳࡶͳΒRiverpod • (iOS։ൃͷࣄྫ͔Β)ϚϧνϞδϡʔϧԽͯ͠ϛχΞϓϦΛ࡞Δ͜ͱ ͕ٻΊΒΕΔΑ͏ʹͳΔՄೳੑ
  63. ࢀߟ • Flutter SDKެࣜαΠτ- https://api. fl utter.dev/ • Flutter Developer

    ެࣜυΩϡϝϯτ - https:// fl utter.dev/development • iOSΞϓϦઃܭύλʔϯೖ໳ - https://peaks.cc/books/iOS_architecture • iOSDC ػೳ͝ͱʹಈ࡞͢ΔϛχΞϓϦͰϓϨϏϡʔαΠΫϧΛര଎ʹͨ͠࿩ - https://speakerdeck.com/aomathwift/ji-neng-gotonidong-zuo- suruminiapuridepurebiyusaikuruwobao-su-nisitahua • Flutterͷঢ়ଶ؅ཧख๏ͷબఆ - https://medium.com/ fl utter-jp/ state-1daa7fd66b94 • ίʔυੜ੒Λ༻͍ͨiOSΞϓϦϚϧνϞδϡʔϧԽͷͨΊͷґଘղܾ - https://techlife.cookpad.com/entry/2021/06/16/110000 • Flutter Ͱ࡞ΒΕͨ༑ਓͷΞϓϦ - ⭐ https://github.com/bannzai/Pilll ⭐
  64. ϑΟʔυόοΫ͍ͩ͘͞ʂ

  65. ࣮ྫͱྺ࢙͔ΒֶͿʂ Flutterͷঢ়ଶ؅ཧํ๏ͷબఆ FlutterKaigi 2021 11/29 19:45ʙ entaku / @entaku_0818